﻿'Description:热表排产类
'Copyright (c) : 通力凯顿（北京）系统集成有限公司
'Writer:zhangrj 
'create Date:20240725
'Rewriter:
'Rewrite Date:

Imports DatabaseAccess.DBUtility
Imports BusinessRule.DTO
Imports System.Text
Imports External.APICalling
Imports BusinessRule.Common
Imports Newtonsoft.Json
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Imports System.Linq

Public Class RbApsLimitedCapacityBusiness
    Private m_containerObj As New uLMSContainerBusiness
    Private rbFactoryID = System.Configuration.ConfigurationManager.AppSettings("RBFactoryID").ToString() '热表ID

    ''' <summary>
    ''' 获取可排产的批次
    ''' </summary>
    ''' <param name="param"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetApsPlanContainerData(ByVal param As Dictionary(Of String, String)) As uMESProcessResult
        Dim result As New uMESProcessResult
        Dim sql As New StringBuilder
        sql.AppendFormat(<stirng>
                            SELECT * FROM (
                            --厂外协作批次
                            SELECT NVL(cci.apspoolid,ao.apspoolid) apspoolid, cci.apssequence, cci.containerid, cci.containername,
                            cci.productid,cci.productname,cci.workflowid,cci.workflowname,cci.workflowname || ':' || cci.workflowrevision workflowinfo,
                            '' facontainername, '' fafactoryname,'' fafactoryid,cci.workflowstepname,
                            NULL shipplanstarttime, NULL shipplanendtime, ai.starttime, ai.endtime, '' nextcontainername, 0 isunlimit,
                            m.plannedcompletiondate, cci.ExpectMaterialDate, ctl.logmessage, NVL(w.image,'0') image
                            FROM containercurrentinfo cci
                            LEFT JOIN container c ON c.containerid = cci.containerid
                            LEFT JOIN workflow w ON w.workflowid = cci.workflowid
                            LEFT JOIN mfgorder m ON m.mfgorderid = c.mfgorderid
                            LEFT JOIN apsplaninfo ai ON ai.containerid = c.containerid
                            LEFT JOIN apspoolworkcenter aw ON aw.workcenterid = cci.workcenterid
                            LEFT JOIN apspool ao ON ao.apspoolid = aw.apspoolid
                            LEFT JOIN ContainerScheduledTaskLogs ctl on ctl.containerid = cci.containerid and ctl.islatest = 1 and ctl.tasktype = 1
                            WHERE cci.containerstatus = 1 AND cci.isinpool = 1 AND cci.isinstore = 0 AND cci.originalfactoryid = '{0}'
                            AND EXISTS (SELECT 1 FROM Userattribute u WHERE u.parentid = c.containerid AND u.userattributename = '委托单位')
                            AND NOT EXISTS(SELECT 1 FROM apsplandetail ap WHERE ap.containerid = cci.containerid)
                            UNION ALL 
                            --转工子卡
                            SELECT Distinct NVL(cci.apspoolid,ao.apspoolid) apspoolid, cci.apssequence, cci.containerid, cci.containername,
                            cci.productid,cci.productname,cci.workflowid,cci.workflowname,cci.workflowname || ':' || cci.workflowrevision workflowinfo, 
                            fac.containername facontainername, f.factoryname fafactoryname,f.factoryid fafactoryid,cci.workflowstepname,
                            ap.starttime shipplanstarttime, ap.endtime shipplanendtime, ai.starttime, ai.endtime,to_char(ca.attributevalue) nextcontainername,
                            DECODE(aui.apsunlimitedinfoid,NULL,0,1) isunlimit,ap.endtime plannedcompletiondate, cci.ExpectMaterialDate, ctl.logmessage, NVL(w.image,'0') image
                            FROM containercurrentinfo cci
                            LEFT JOIN container c ON c.containerid = cci.containerid
                            LEFT JOIN workflow w ON w.workflowid = cci.workflowid
                            LEFT JOIN apsplaninfo ai ON ai.containerid = c.containerid
                            LEFT JOIN containerattribute ca ON ca.containerid = c.containerid AND ca.attributename = '下序批次名' --查询连续转工序的下一序的子卡批次名
                            LEFT JOIN apspoolworkcenter aw ON aw.workcenterid = cci.workcenterid
                            LEFT JOIN apspool ao ON ao.apspoolid = aw.apspoolid
                            INNER JOIN containersubcontainer csc ON csc.subcontainerid = c.containerid
                            LEFT JOIN container fac ON fac.containerid = csc.containerid --父批次信息
                            LEFT JOIN Factory f ON f.factoryid = fac.originalfactoryid
                            LEFT JOIN apsplandetail ap ON ap.containerid = fac.containerid AND ap.workflowstepname = csc.workflowstepname 
                            LEFT JOIN apsunlimitedinfo aui ON aui.apsplandetailid = ap.apsplandetailid AND aui.status = 0
                            LEFT JOIN ContainerScheduledTaskLogs ctl on ctl.containerid = cci.containerid and ctl.islatest = 1 and ctl.tasktype = 1
                            WHERE cci.containerstatus = 1 AND cci.isinpool = 1 AND cci.isinstore = 0 AND ap.isuse = 0 AND ap.status = '3'
                            AND cci.originalfactoryid = '{0}'
                            AND NOT EXISTS(SELECT 1 FROM apsplandetail apd WHERE apd.containerid = cci.containerid)
                            UNION ALL
                            --返修卡
                            SELECT Distinct NVL(cci.apspoolid,ao.apspoolid) apspoolid, cci.apssequence, cci.containerid, cci.containername,
                            cci.productid,cci.productname,cci.workflowid,cci.workflowname,cci.workflowname || ':' || cci.workflowrevision workflowinfo, 
                             '' facontainername, '' fafactoryname,'' fafactoryid,cci.workflowstepname,
                            NULL shipplanstarttime, NULL shipplanendtime, ai.starttime, ai.endtime, '' nextcontainername, 0 isunlimit,
                            NULL plannedcompletiondate, cci.ExpectMaterialDate, ctl.logmessage, NVL(w.image,'0') image
                            FROM containercurrentinfo cci
                            LEFT JOIN container c ON c.containerid = cci.containerid
                            LEFT JOIN workflow w ON w.workflowid = cci.workflowid
                            LEFT JOIN apsplaninfo ai ON ai.containerid = c.containerid
                            LEFT JOIN apspoolworkcenter aw ON aw.workcenterid = cci.workcenterid
                            LEFT JOIN apspool ao ON ao.apspoolid = aw.apspoolid
                            INNER JOIN userattribute u ON u.parentid = cci.containerid AND u.userattributename = '返修主批次ID'       
                            LEFT JOIN ContainerScheduledTaskLogs ctl on ctl.containerid = cci.containerid and ctl.islatest = 1 and ctl.tasktype = 1
                            WHERE cci.containerstatus = 1 AND cci.isinpool = 1 AND cci.isinstore = 0 
                            AND cci.originalfactoryid = '{0}'
                            AND NOT EXISTS(SELECT 1 FROM apsplandetail apd WHERE apd.containerid = cci.containerid)
                            ) co WHERE 1 = 1
                       </stirng>.Value, rbFactoryID)

        If param.ContainsKey("PoolID") Then
            If Not String.IsNullOrWhiteSpace(param("PoolID")) Then
                sql.AppendFormat(" AND co.apspoolid = '{0}'", param("PoolID"))
            End If
        End If

        If param.ContainsKey("MainFactoryID") Then '主制分厂
            If Not String.IsNullOrWhiteSpace(param("MainFactoryID")) Then
                sql.AppendFormat(" AND co.fafactoryid = '{0}'", param("MainFactoryID"))
            End If
        End If

        If param.ContainsKey("MfgOrderName") Then '生产订单号
            If Not String.IsNullOrWhiteSpace(param("MfgOrderName")) Then
                sql.AppendFormat(" AND co.MfgOrderName = '{0}'", param("MfgOrderName"))
            End If
        End If

        If param.ContainsKey("ContainerName") Then '批次名
            If Not String.IsNullOrWhiteSpace(param("ContainerName")) Then
                sql.AppendFormat(" AND co.ContainerName like '%{0}%'", param("ContainerName"))
            End If
        End If

        If param.ContainsKey("FaContainerName") Then '父卡批次
            If Not String.IsNullOrWhiteSpace(param("FaContainerName")) Then
                sql.AppendFormat(" AND co.facontainername like '%{0}%'", param("FaContainerName"))
            End If
        End If

        If param.ContainsKey("IsApsSended") Then '是否排产下发
            If param("IsApsSended") = "1" Then '不包含已经排产下发的数据
                sql.AppendLine(" AND NOT EXISTS(SELECT 1 FROM apsplandetail2 apd WHERE apd.containerid = co.containerid AND apd.status = '1')")
            End If
        End If

        If param.ContainsKey("IsAps") Then '是否排产过
            If param("IsAps") = "1" Then '不包含已经排产的数据
                sql.AppendLine(" AND NOT EXISTS(SELECT 1 FROM apsplandetail2 apd WHERE apd.containerid = co.containerid)")
            End If
        End If

        If param.ContainsKey("IsContinuityContainer") Then '是否查询连续批次
            If param("IsContinuityContainer") = "1" Then '不查询处于连续批次中的非首位批次
                sql.AppendLine(" AND NOT EXISTS(SELECT 1 FROM containerattribute ca WHERE ca.attributename = '下序批次名' AND to_char(ca.attributevalue) = co.containername)")
            End If
        End If

        sql.AppendLine(" ORDER BY co.apssequence, co.containername")

        If Boolean.Parse(param("IsPaging")) Then '是否分页
            Dim pageDto As New uMESPagingDataDTO()
            pageDto.CurrentPageIndex = Integer.Parse(param("pageIndex"))
            pageDto.PageSize = Integer.Parse(param("pageSize"))
            pageDto.strSQL = sql.ToString()
            result = DatabaseHelper.RunProcedureForPage(pageDto)
        Else
            result.Result = True
            result.DBTable = DatabaseHelper.Query_ReturnDatTable(sql.ToString())
        End If
        Return result
    End Function

    ''' <summary>
    ''' 计算批次排产优先级（顺序号）
    ''' </summary>
    ''' <param name="dic"></param>
    ''' <param name="msg"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function CalcContainerApsPriority(ByVal dic As Dictionary(Of String, String), ByRef msg As String) As Boolean
        Dim dtContainer = GetApsPlanContainerData(dic).DBTable '获取全部批次
        If dtContainer.Rows.Count = 0 Then
            msg = "排产池未有批次数据"
            Return False
        End If
        Dim strsql As String =
            String.Format("select prioritycode,priorityname,sequence from apspoolpriority apr where apr.apspoolid = '{0}' order by apr.sequence asc", dic("PoolID"))
        Dim dtApsPool As DataTable = DatabaseHelper.Query_ReturnDatTable(strsql)
        If dtApsPool.Rows.Count = 0 Then
            msg = "没有排产策略" '没有排产策略
            Return False
        End If

        '清空旧的排产记录
        strsql = String.Format("update containercurrentinfo cci set cci.apspoolid = null, cci.apssequence = null where apspoolid = '{0}'", dic("PoolID"))
        DatabaseHelper.ExecuteSql(strsql)
        '开始计算
        Dim isContinuityShip = If(dtApsPool.AsEnumerable().Where(Function(p) p("prioritycode").ToString() = "10").Count > 0, True, False) '是否有连续转工优先
        Dim isWorkingTime = If(dtApsPool.AsEnumerable().Where(Function(p) p("prioritycode").ToString() = "11").Count > 0, True, False) '是否有加工时间短优先
        Dim isPreFinish = If(dtApsPool.AsEnumerable().Where(Function(p) p("prioritycode").ToString() = "13").Count > 0, True, False) '是否有最早交货优先
        '添加几个优先策略的列
        dtContainer.Columns.Add("ContinuityShip") '连续转工优先
        dtContainer.Columns.Add("ContinuityShipCon") '连续转工的第一个批次
        dtContainer.Columns.Add("ContinuityShipConSort") '连续转工内部排序
        'dtContainer.Columns.Add("ShipRelease") '转工放行优先（用是否无限制字段判断即可）
        dtContainer.Columns.Add("WorkingTime") '加工时间短优先
        'dtContainer.Columns.Add("PreFinish") '最早交货优先（转工子卡批次用转工计划结束日期，采购订单批次用订单计划结束日期）
        '计算连续转工优先
        Dim dtNewContainer = dtContainer.Clone '先克隆一个新的批次表，表示最终排序
        For Each dr As DataRow In dtContainer.Rows

            If dtNewContainer.Select(String.Format("containername = '{0}'", dr("containername").ToString())).Length > 0 Then '如果批次已经计算过，则跳过
                Continue For
            End If

            Dim conList = GetContinuityContainerName(dr("containername").ToString()) '获取连续转工序子卡批次
            If conList.Count > 1 Then '如果是连续转工子卡，则需要考虑连续的批次计算
                Dim workingTime = 0 '加工时长，等于所有连续转工子卡的加工时长总和
                Dim plannedcompletiondate = "" '计划完成日期 
                If isPreFinish Then '如果有最早交货优先，则计算
                    strsql = String.Format(<string>
                            SELECT NVL(ap.endtime,SYSDATE) plannedcompletiondate
                            FROM containercurrentinfo cci
                            LEFT JOIN containersubcontainer csc ON csc.subcontainerid = cci.containerid
                            LEFT JOIN container fac ON fac.containerid = csc.containerid --父批次信息
                            LEFT JOIN apsplandetail ap ON ap.containerid = fac.containerid AND ap.workflowstepname = csc.workflowstepname AND ap.isuse = 0
                            WHERE cci.containername = '{0}'
                          </string>.Value, conList.Last())
                    plannedcompletiondate = DatabaseHelper.Query_ReturnDatTable(strsql).Rows(0)("plannedcompletiondate").ToString() '计划完成时间取最后一个批次的计划完成时间
                End If
                If isWorkingTime Then '如果有加工时长优先，则计算
                    For index = 0 To conList.Count - 1
                        workingTime += CalaTotalStepInfoWorkingTime(conList(index))
                    Next
                End If
                Dim image = GetWorkflowImage(conList(0)) '只取第一个转工子卡的工艺温度即可
                For index = 0 To conList.Count - 1
                    Dim row As DataRow = dtNewContainer.NewRow
                    row("containername") = conList(index)
                    row("ContinuityShip") = If(isContinuityShip, 1, 0)
                    row("ContinuityShipCon") = conList(0) '连续转工序子卡都记录第一个批次名，方便排序
                    row("ContinuityShipConSort") = (index + 1) '连续转工内部排序
                    row("isunlimit") = Integer.Parse(dr("isunlimit").ToString()) '转工放行取其值即可
                    row("WorkingTime") = workingTime
                    If Not String.IsNullOrWhiteSpace(plannedcompletiondate) Then
                        row("plannedcompletiondate") = Date.Parse(plannedcompletiondate)
                    End If
                    row("image") = image
                    dtNewContainer.Rows.Add(row)
                Next
            Else '--------不是连续转工子卡，则需要计算其加工时长。
                dr("ContinuityShip") = 0 '不是连续转工子卡
                dr("WorkingTime") = If(isWorkingTime, CalaTotalStepInfoWorkingTime(dr("containername").ToString()), 0) '计算加工时长
                dtNewContainer.Rows.Add(dr.ItemArray()) '直接添加改行
            End If
        Next

        Dim strSort As String = String.Empty '最终排序
        For Each drA As DataRow In dtApsPool.Rows
            If drA("prioritycode").ToString = "10" Then '连续转工优先
                strSort += "ContinuityShip DESC,"
            End If

            If drA("prioritycode").ToString = "11" Then  '转工放行优先（用是否无限制字段判断即可）
                strSort += "isunlimit DESC,"
            End If

            If drA("prioritycode").ToString = "12" Then '加工时间短优先
                strSort += "WorkingTime ASC,"
            End If

            If drA("prioritycode").ToString = "13" Then '最早交货优先（转工子卡批次用转工计划结束日期，采购订单批次用订单计划结束日期）
                strSort += "plannedcompletiondate ASC," '（注意：如果日期为空，会排在前面）
            End If

            If drA("prioritycode").ToString = "14" Then '温度优先（温度低的优先）
                strSort += "image ASC,"
            End If
        Next
        strSort += "ContinuityShipCon, ContinuityShipConSort ASC" '最终在连续转工子卡中再排序两次
        Dim dvContainer = dtNewContainer.AsDataView()
        dvContainer.Sort = strSort
        Dim dtSortContainer = dvContainer.ToTable()

        '批量更新
        Dim sqlList As New ArrayList
        Dim sql As String =
            <string>
                update containercurrentinfo cci 
                set apspoolid = '{0}', ApsSequence = '{1}'
                where cci.containername = '{2}'  
            </string>
        For index = 0 To dtSortContainer.Rows.Count - 1
            sqlList.Add(String.Format(sql, dic("PoolID"), (index + 1) * 5, dtSortContainer.Rows(index)("containername").ToString()))
            If sqlList.Count > 999 Then '每一千个提交一次
                DatabaseHelper.ExecuteSqlTran(sqlList)
                sqlList.Clear()
            End If
        Next
        '执行更新
        If sqlList.Count > 0 Then
            DatabaseHelper.ExecuteSqlTran(sqlList)
        End If
        Return True
    End Function

    ''' <summary>
    ''' 获取连续加工批次列表
    ''' </summary>
    ''' <param name="containerName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetContinuityContainerName(ByVal containerName As String) As List(Of String)
        Dim list As New List(Of String)
        Dim preList = GetPreContainerName(containerName)
        If preList.Count > 0 Then
            list.AddRange(preList) '添加所有的上序批次号
        End If
        list.Add(containerName) '添加当前批次号
        Dim nextList = GetNextContainerName(containerName)
        If nextList.Count > 0 Then
            list.AddRange(nextList) '添加所有的下序批次号
        End If
        Return list
    End Function

    ''' <summary>
    ''' 获取连续转工序的上一个子卡批次
    ''' </summary>
    ''' <param name="containerName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetPreContainerName(ByVal containerName As String) As List(Of String)
        Dim list As New List(Of String)
        Dim sql = String.Format(<string>
                                    SELECT cci.containername FROM containerattribute ca
                                    LEFT JOIN containercurrentinfo cci ON cci.containerid = ca.containerid
                                    WHERE to_char(ca.attributevalue) = '{0}' AND ca.attributename = '下序批次名' AND cci.containerstatus = 1 AND cci.isinstore != 1
                                </string>.Value, containerName)
        Dim dtpre = DatabaseHelper.Query_ReturnDatTable(sql)
        If dtpre.Rows.Count > 0 Then
            Dim nextList = GetPreContainerName(dtpre.Rows(0)("containername").ToString())
            If nextList.Count > 0 Then
                list.AddRange(nextList)
            End If
            list.Add(dtpre.Rows(0)("containername").ToString())
        End If
        Return list
    End Function

    ''' <summary>
    ''' 获取连续转工序的下一个子卡批次
    ''' </summary>
    ''' <param name="containerName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetNextContainerName(ByVal containerName As String) As List(Of String)
        Dim list As New List(Of String)
        Dim sql = String.Format(<string>
                                    SELECT ca.attributevalue,cci1.containername FROM containerattribute ca
                                    LEFT JOIN containercurrentinfo cci ON cci.containerid = ca.containerid
                                    LEFT JOIN containercurrentinfo cci1 ON cci1.containername = to_char(ca.attributevalue) --下序批次
                                    WHERE cci.containername = '{0}' AND ca.attributename = '下序批次名' AND cci1.containerstatus = 1 AND cci1.isinstore != 1
                                </string>.Value, containerName)
        Dim dtnext = DatabaseHelper.Query_ReturnDatTable(sql)

        If dtnext.Rows.Count > 0 Then
            list.Add(dtnext.Rows(0)("attributevalue").ToString())
            Dim nextList = GetNextContainerName(dtnext.Rows(0)("containername").ToString())
            If nextList.Count > 0 Then
                list.AddRange(nextList)
            End If
        End If
        Return list
    End Function

    ''' <summary>
    ''' 计算批次的所有工步加工完的时长（加工周期）
    ''' </summary>
    ''' <param name="containerName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function CalaTotalStepInfoWorkingTime(ByVal containerName As String) As Integer
        Dim sql = String.Format(<stirng>
                        SELECT NVL(s.makecycle,0) makecycle,s.stepinfoid,NVL(r.isinfinite,0) isinfinite FROM containercurrentinfo cci
                        LEFT JOIN container c ON c.containerid = cci.containerid
                        LEFT JOIN workflowstep ws ON ws.workflowid = DECODE(cci.isinstore,-1,c.mfgworkflowid,cci.workflowid)
                        LEFT JOIN Userattribute u ON u.parentid = c.containerid AND u.userattributename = '开始工步顺序号' 
                        LEFT JOIN stepinfo s ON s.workflowstepid = ws.workflowstepid AND s.stepseq >= NVL(u.attributevalue,0)
                        INNER JOIN stepinfobase sb ON sb.revofrcdid = s.Stepinfoid --取最新版
                        LEFT JOIN stepresourceinfo st ON st.stepid = s.stepinfoid
                        LEFT JOIN resourcedef r ON r.resourceid = st.resourceid
                        WHERE cci.containername = '{0}'
                  </stirng>.Value, containerName)
        Dim dtMc = DatabaseHelper.Query_ReturnDatTable(sql)
        Dim workingTime = 0
        Dim dtStepinfo = dtMc.AsDataView().ToTable(True, "stepinfoid") '去重工步，循环每个工步
        For Each dr As DataRow In dtStepinfo.Rows
            Dim dtCurrStep = dtMc.Select(String.Format("stepinfoid = '{0}'", dr("stepinfoid").ToString())).CopyToDataTable() '查出工步的所有数据
            If dtCurrStep.AsEnumerable().Where(Function(p) Integer.Parse(p("isinfinite").ToString()) = 1).Count = 0 Then '如果工步下的设备没有无限能力设备，则累计该工步的加工时间
                workingTime += Integer.Parse(dtCurrStep.Rows(0)("makecycle").ToString())
            End If
        Next
        Return workingTime
    End Function

    ''' <summary>
    ''' 获取工艺温度
    ''' </summary>
    ''' <param name="containerName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetWorkflowImage(ByVal containerName As String) As String
        Dim sql = String.Format(<string>
                                    SELECT NVL(w.image, '0') image FROM containercurrentinfo c 
                                    LEFT JOIN workflow w ON w.workflowid = c.workflowid
                                    WHERE c.containername = '{0}'
                                </string>.Value, containerName)
        Return DatabaseHelper.Query_ReturnDatTable(sql).Rows(0)("image").ToString()
    End Function

#Region "排产计算"
    Protected recursionStartTime As Date = Date.Now '递归时间标记，用于防止无限递归
    Protected Sql_ApsPlanSelect As String = <string> select * from ApsPlanInfo ai where ai.containerid = '{0}' and ai.ApsType2 = 2 and ai.ApsType = '1' and ai.Isuse=0 </string>.Value

    Protected Sql_ApsPlanInsert As String = <STIRNG> insert into ApsPlanInfo ai (ai.ApsType2,ai.apsplaninfoid,ai.apstype,ai.containerid,ai.createdate,ai.createemployeeid,ai.endtime,ai.isuse,
                                             ai.lastchangedate,ai.notes,ai.starttime,ai.workflowid) values (2,'{0}','1','{1}',sysdate,'{2}',to_date('{3}','YYYY-MM-DD HH24:MI:SS'),0,
                                             SYSDATE,'{4}',to_date('{5}','YYYY-MM-DD HH24:MI:SS'),'{6}') </STIRNG>.Value
    Protected Sql_ApsPlanUpdate As String = <string>update ApsPlanInfo ai set ai.starttime = to_date('{0}','YYYY-MM-DD HH24:MI:SS') , ai.endtime = to_date('{1}','YYYY-MM-DD HH24:MI:SS'), 
                                                    ai.lastchangedate = sysdate where ai.apsplaninfoid='{2}'</string>.Value

    Protected Sql_ApsDetailSelectAll As String = <string>select * from ApsPlanDetail2 ad where ad.apsplaninfoid = '{0}' and ad.isuse=0 </string>.Value

    Protected Sql_ApsDetailSelect As String = <string>select ad.* from ApsPlanDetail2 ad where ad.apsplandetailid = '{0}' and ad.isuse=0 </string>.Value

    Protected Sql_ApsDetailInsert As String = <string>insert into ApsPlanDetail2 ad (ad.CreateDate,ad.LastChangeDate,ad.apsplandetailid,ad.apsplaninfoid,ad.containerid,ad.starttime,ad.endtime,ad.isuse,
                                                    ad.workflowstepid,ad.workflowstepname,ad.notes,ad.resourceid,CreateEmployeeID,ad.Resstarttime,ad.Resendtime,ad.qty,ad.status,ad.stepinfoid,ad.stepinfodesc,ad.stepinfono,
                                                    ad.stepinfofactoryid,ad.workcenterid,ad.teamid) 
                                                    values (sysdate,sysdate,'{0}','{1}','{2}',to_date('{3}','YYYY-MM-DD HH24:MI:SS'),to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,'{5}','{6}','{7}','{8}','{9}',to_date('{10}','YYYY-MM-DD HH24:MI:SS'),to_date('{11}','YYYY-MM-DD HH24:MI:SS'),{12},0,'{13}','{14}','{15}','{16}','{17}','{18}') </string>.Value
    Protected Sql_ApsDetailUpdate As String = <string>update ApsPlanDetail2 ad SET ad.LastChangeDate = sysdate, ad.starttime = to_date('{0}','YYYY-MM-DD HH24:MI:SS'), ad.endtime = to_date('{1}','YYYY-MM-DD HH24:MI:SS'),
                                                     ad.resourceid = '{2}', ad.Resstarttime = to_date('{3}','YYYY-MM-DD HH24:MI:SS'), ad.Resendtime = to_date('{4}','YYYY-MM-DD HH24:MI:SS'), ad.qty = {5}, ad.stepinfofactoryid = '{6}',
                                                     ad.workcenterid = '{7}',ad.teamid = '{8}' WHERE ad.apsplandetailid = '{9}' </string>.Value

    Protected Sql_ApsDetailDel As String = <string>delete ApsPlanDetail2 ae where ae.apsplandetailid='{0}'</string>.Value

    Protected Sql_TaskSelect As String = <stirng>
                                             SELECT sa.attributevalue FROM scheduledtask st 
                                             LEFT JOIN scheduledtaskattr sa ON sa.scheduledtaskid = st.scheduledtaskid AND sa.isuse = 0
                                             WHERE st.tasktype = 7 AND sa.attributename = '连续加工间隔时间' AND st.isuse = 0
                                         </stirng>.Value

    Protected Sql_StepApsRange As String = <string>select ad.containerid,min(ad.starttime) minstarttime,max(ad.endtime) maxendtime FROM ApsPlanDetail2 ad WHERE ad.apsplaninfoid = '{0}' and ad.isuse = 0 group by ad.containerid </string>.Value

    Protected Sql_SameRes = <string>
                                SELECT ap.resourceid FROM apsplaninfo ai
                                LEFT JOIN container c ON c.containerid = ai.containerid
                                LEFT JOIN apsplandetail2 ap ON ap.apsplaninfoid = ai.apsplaninfoid
                                WHERE c.productid = '{0}' AND ai.workflowid = '{1}' AND c.rbtogetherflag = '{2}' AND ap.stepinfoid = '{3}' AND c.containerid != '{4}'
                            </string>.Value

    ''' <summary>
    ''' 计算批次的排产信息
    ''' </summary>
    ''' <param name="dtContainer">计算的批次表</param>
    ''' <param name="userInfo">用户信息</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function CalcContainersApsPlan(ByVal dtContainer As DataTable, ByVal userInfo As CurrentUserInfo) As uMESProcessResult
        Dim result As New uMESProcessResult
        Dim dtTask = DatabaseHelper.Query_ReturnDatTable(Sql_TaskSelect)
        If dtTask.Rows.Count = 0 Then
            result.Message = "未配置连续加工间隔时间"
            Return result
        End If
        Dim continuityTime = Integer.Parse(dtTask.Rows(0)("attributevalue").ToString()) '连续加工间隔时间
        Dim startTime As String '开始日期，默认为空
        dtContainer.Columns.Add("IsApsRecode") '是否已排产
        For Each drCon As DataRow In dtContainer.Rows
            If drCon("IsApsRecode").ToString() = "1" Then '如果已排产，跳过
                Continue For
            End If
            startTime = "" '每个批次开始时间初始化为空，在计算时才获取
            Dim containerNameList = GetContinuityContainerName(drCon("ContainerName").ToString()) '获取连续批次名（如果不是连续转工批次，只会有一条数据，同样适合循环）
            For i = 0 To containerNameList.Count - 1 '循环连续转工批次，依次计算
                Dim drCons = dtContainer.Select(String.Format("ContainerName = '{0}'", containerNameList(i)))
                For Each dc As DataRow In drCons
                    dc("IsApsRecode") = "1" '将批次标记为已排产，循环到该批次时不用再排产一次
                Next
                If Not String.IsNullOrWhiteSpace(startTime) Then
                    startTime = Date.Parse(startTime).AddMinutes(continuityTime).ToString() '开始日期追加连续加工间隔时间
                End If
                result = CalcSingeContainerApsPlan(containerNameList(i), startTime, userInfo) '单个批次进行计算
                If Not result.Result And result.ReturnData = 0 Then '如果出错，则退出连续转工批次计算（如果ReturnData是1，表示该批次已加工或者已下发，不用退出连续转工的计算）
                    Exit For
                End If
            Next
        Next
        Return result
    End Function

    ''' <summary>
    ''' 单个批次排产计算
    ''' </summary>
    ''' <param name="containerName">计算的批次名</param>
    ''' <param name="startTime">排产计算初始开始时间</param>
    ''' <param name="userInfo">用户信息</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function CalcSingeContainerApsPlan(ByVal containerName As String, ByRef startTime As String, ByVal userInfo As CurrentUserInfo) As uMESProcessResult
        Dim result As New uMESProcessResult
        result.Result = True
        result.ReturnData = 0
        '************************
        '------获取批次数据
        '************************
        Dim conBaseDt As DataTable = Aps_GetConBaseDt(containerName) '批次基本信息
        If conBaseDt.Rows.Count = 0 Then
            result.Result = False
            result.Message = String.Format("未查询到批次：{0}的数据。", containerName)
            Return result '不用记录在日志
        End If
        Dim conBaseRow = conBaseDt.Rows(0) '批次信息

        If Not DatabaseHelper.GetSingle(String.Format("SELECT 1 FROM containerattribute ca WHERE ca.containerid = '{0}' AND ca.attributename = '排产状态' AND to_char(ca.attributevalue) = '排产中' ", conBaseRow("ContainerID").ToString())) Is Nothing Then
            result.Result = False
            result.Message = String.Format("批次：{0}正在排产中，请稍后再试。", containerName)
            Return result '不用记录在日志
        End If

        '************************
        '------获取可排产的工步数据
        '************************
        Dim res = GetCanApsStepinfoData(conBaseRow("ContainerID").ToString()) '获取可排产的所有工步
        If Not res.Item1 Then
            result.Result = False
            result.Message = res.Item2
            result.ReturnData = res.Item3
            GoTo EndL '跳转到末尾
        End If
        Dim dtStepInfo = res.Item4 '工步列表
        '************************
        '------删除其他工艺版本的排产数据，只会有一个工艺版本的排产数据
        '************************
        DeleOtherRevApsPlanData(conBaseRow)
        '************************
        '------获取已排产的数据
        '************************
        Dim conApsID = Guid.NewGuid.ToString
        Dim dtApsInfo As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ApsPlanSelect, conBaseRow("containerid").ToString)) '批次的计划信息
        If dtApsInfo.Rows.Count > 0 Then
            conApsID = dtApsInfo.Rows(0)("ApsPlanInfoID").ToString
        End If
        Dim dtApsDetail = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ApsDetailSelectAll, conApsID)) '获取已排产明细
        '************************
        '------获取批次的日历，排产池，截止日期
        '************************
        Dim faMakeCalendarDt = Aps_GetMakeCalendarDt(conBaseRow("makecalendarid").ToString) '批次所在分厂的日历
        Dim apsPoolDt = Aps_GetApsPoolDt(conBaseRow("apspoolid").ToString) '排产池信息
        Dim deadlineDate As Date = New Date(9999, 1, 1) '排产截止日期
        If apsPoolDt.Rows.Count > 0 Then
            If Not apsPoolDt.Rows(0).IsNull("OfficialCycle") Then
                deadlineDate = Date.Now.Date.AddDays(Val(apsPoolDt.Rows(0)("OfficialCycle")) + 1)
            End If
        End If
        '************************
        '------获取批次的开始日期（如果批次有排产记录，则取排产记录中已下发的最大结束日期作为开始日期）--小记：如果排除的工步没有排产数据，只有生产数据，这种情况怎么计算？
        '************************
        Dim drSendAps = dtApsDetail.Select("status = '1'")
        If drSendAps.Length > 0 Then
            startTime = drSendAps.AsEnumerable().Max(Function(p) Date.Parse(p("endtime").ToString)).ToString("yyyy-MM-dd HH:mm:ss")
        Else
            startTime = If(String.IsNullOrWhiteSpace(startTime), GetConApsStartTime(conBaseRow), startTime) '获取真正的开始日期
        End If
        '************************
        '------批次添加排产状态属性，表示批次正在排产中
        '************************
        m_containerObj.SaveContainerAttr(conBaseRow("ContainerID").ToString(), "排产状态", "排产中")
        '************************
        '------循环工步计算开始
        '************************
        Dim sqlList As New ArrayList '用于存储多个sql，统一提交执行
        For Each drStep As DataRow In dtStepInfo.Rows
            Try
                Dim currTime = Date.Parse(startTime) '当前工步的开始时间，赋值开始时间
                Dim stepResourceIds = String.Empty '全部设备ID字符串
                Dim dtStepRecource As DataTable = Aps_GetSpecResourceDt(drStep("stepinfoid").ToString(), conBaseRow("productname").ToString()) '获取工步设备信息
                If dtStepRecource.Rows.Count = 0 Then
                    result.Result = False
                    result.Message = String.Format("批次：{0}的工步：{1}-{2}未关联设备", containerName, drStep("stepno").ToString(), drStep("description").ToString())
                    Exit For
                End If
                For Each drRec As DataRow In dtStepRecource.Rows
                    stepResourceIds += String.Format("'{0}',", drRec("resourceid").ToString())
                Next
                stepResourceIds = stepResourceIds.TrimEnd(",")
                '************************
                '------此工步设备当前时间前所有占用信息，排除已完工的
                '************************
                Dim apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, conBaseRow, currTime.ToString("yyyy-MM-dd HH:mm:ss"))
                '************************
                '------获取工步设备的保养信息
                '************************
                Dim stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, currTime.ToString("yyyy/MM/dd HH:mm:ss"))
                '************************
                '------此工序设备所有的日历信息
                '************************
                Dim stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)
                '************************
                '------此工步设备所有的班次信息
                '************************
                Dim stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)
                '************************
                '------计算工步的加工周期以及设备的加工周期
                '************************
                GetStepCycle(conBaseRow, drStep, dtStepRecource)
                '************************
                '------设备模拟加工
                '************************
                Dim resSimulationDt As DataTable = LMAscSimulationByRes(conBaseRow, drStep, dtStepRecource, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, currTime, currTime)
                Dim resSimulationDt2 = resSimulationDt.AsEnumerable().Where(Function(resSLRow) Date.Parse(resSLRow("simulateEnd").ToString()).Date < deadlineDate)
                If resSimulationDt2.Count = 0 Then
                    '到截止日期后，若后续的工步还有以前的排产数据，则应该清除
                    DeleteApsPlanData(conApsID, Integer.Parse(drStep("stepseq").ToString))
                    Exit For
                End If
                resSimulationDt = resSimulationDt2.CopyToDataTable '设备加工后的数据
                '************************
                '------获取最优的设备模拟加工（多个设备中最早结束的设备）
                '************************
                'Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, dtStepRecource)
                Dim newestDate = resSimulationDt.AsEnumerable().Min(Function(p) Date.Parse(p("simulateEnd").ToString))
                Dim dtNewestRes = resSimulationDt.AsEnumerable().Where(Function(p) Date.Parse(p("simulateEnd").ToString) = newestDate).CopyToDataTable() '获取最优的所有设备
                Dim dvNewestRes = dtNewestRes.AsDataView()
                dvNewestRes.Sort = "combinebatch desc,qty desc" '最优设备里排序，组批优先，然后设备装炉量优先
                Dim newestRow = dvNewestRes.ToTable().Rows(0)
                '************************
                '------判断是否存在相同工艺，相同热表大批次的其他批次存在排产记录，存在且和最优设备列表中有相同的设备，则优先该设备
                '************************
                Dim dtSameRes = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_SameRes, conBaseRow("productid").ToString(), conBaseRow("workflowid").ToString(), conBaseRow("rbtogetherflag").ToString(), drStep("stepinfoid").ToString(), conBaseRow("containerid").ToString()))
                If dtSameRes.Rows.Count > 0 And dtNewestRes.Select("combinebatch = '1'").Length = 0 Then
                    Dim sameRes = dtNewestRes.Select(String.Format("resourceid = '{0}'", dtSameRes.Rows(0)("resourceid").ToString()))
                    If sameRes.Length > 0 Then
                        newestRow = sameRes(0)
                    End If
                End If
                '************************
                '------添加工步排产信息保存SQL
                '************************
                Dim stepApsId = String.Empty
                Dim apsDetailRow = dtApsDetail.Select(String.Format("stepinfoid = '{0}'", drStep("stepinfoid").ToString()))
                If apsDetailRow.Length > 0 Then
                    stepApsId = apsDetailRow(0)("apsplandetailid").ToString()
                    sqlList.Add(String.Format(Sql_ApsDetailUpdate, newestRow("simulateStart").ToString(), newestRow("simulateEnd").ToString(), newestRow("resourceid").ToString(), newestRow("simulateStart2").ToString(),
                                              newestRow("simulateEnd2").ToString(), conBaseRow("qty").ToString, drStep("stepinfofactoryid").ToString(), drStep("workcenterid").ToString(), drStep("teamid").ToString(), stepApsId))

                Else
                    stepApsId = Guid.NewGuid.ToString()
                    sqlList.Add(String.Format(Sql_ApsDetailInsert, stepApsId, conApsID, conBaseRow("containerid").ToString, newestRow("simulateStart").ToString(), newestRow("simulateEnd").ToString(),
                                                    drStep("workflowstepid").ToString, drStep("workflowstepname").ToString, "", newestRow("resourceid").ToString(), userInfo.EmployeeID,
                                                    newestRow("simulateStart2").ToString(), newestRow("simulateEnd2").ToString(), conBaseRow("qty").ToString,
                                                    drStep("stepinfoid").ToString, drStep("description").ToString, drStep("stepno").ToString, drStep("stepinfofactoryid").ToString(), drStep("workcenterid").ToString, drStep("teamid").ToString))
                End If
                startTime = newestRow("simulateEnd").ToString() '将当前工步的排产结束日期赋给开始日期，作为下一个工步的开始日期
            Catch ex As Exception
                result.Result = False
                result.Message = String.Format("工步：{0}-{1}({2})排产计算报错，原因：{3}", drStep("stepno").ToString, drStep("description").ToString, drStep("stepinfoid").ToString, ex.Message)
                Exit For
            End Try
        Next
        m_containerObj.SaveContainerAttr(conBaseRow("containerid").ToString, "排产状态", "排产完")
        '************************
        '------循环工步计算结束后，执行数据存储或更新
        '************************
        If result.Result Then
            DatabaseHelper.ExecuteSqlTran(sqlList) '将工步的排产信息保存，在执行批次的排产信息
            Try
                '查询批次开始和结束时间，必定有数据
                Dim dtApsRangeTime = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsRange, conApsID))
                If dtApsRangeTime.Rows.Count > 0 Then
                    sqlList.Clear()
                    If dtApsInfo.Rows.Count = 0 Then '如果不存在排产数据，则插入
                        sqlList.Add(String.Format(Sql_ApsPlanInsert, conApsID, conBaseRow("containerid").ToString, userInfo.EmployeeID, dtApsRangeTime.Rows(0)("maxendtime"), "", dtApsRangeTime.Rows(0)("minstarttime"),
                                                           conBaseRow("workflowid").ToString))
                    Else
                        sqlList.Add(String.Format(Sql_ApsPlanUpdate, dtApsRangeTime.Rows(0)("minstarttime"), dtApsRangeTime.Rows(0)("maxendtime"), conApsID))
                    End If
                    DatabaseHelper.ExecuteSqlTran(sqlList)
                    result.Message = "执行成功"
                End If
            Catch ex As Exception
                sqlList.Add(String.Format("delete apsplandetail2 al where al.apsplaninfoid = '{0}'", conApsID))
                sqlList.Add(String.Format("delete ApsPlanInfo where ApsPlanInfoid = '{0}'", conApsID))
                DatabaseHelper.ExecuteSqlTran(sqlList)
                result.Message = "执行报错：" + ex.Message
                result.Result = False
            End Try
        End If
        '************************
        '------记录日志
        '************************
EndL:
        If result.Result Then
            m_containerObj.DeleteContainerSCLog(conBaseRow("containerid").ToString, 1) '排产成功，删除错误日志
        Else
            '排产失败，记录排产错误日志
            m_containerObj.WriteContainerSCLog("", conBaseRow("containerid").ToString, conBaseRow("containername").ToString, result.Message, 1, userInfo.EmployeeID)
        End If

        Return result
    End Function

    ''' <summary>
    ''' 追加模拟设备加工数据优先级，及筛选出优先级最高的设备 （弃用）
    ''' </summary>
    ''' <param name="resSimulationDt">设备模拟加工数据</param>
    ''' <param name="stepReourceDt">工序设备</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function LMAscSLResEmp(ByVal resSimulationDt As DataTable, ByVal stepReourceDt As DataTable) As DataTable
        '此时应取出模拟加工结束最近的设备，把优先级追加上
        Dim recentEndDate = Nothing
        If Not resSimulationDt.Columns.Contains("prioritynum") Then
            resSimulationDt.Columns.Add(New DataColumn("prioritynum", System.Type.GetType("System.Int32")))
        End If

        For Each resSl In resSimulationDt.Rows
            '追加优先级
            If stepReourceDt.Select("resourceid='" + resSl("resourceid").ToString + "'").Length > 0 Then
                resSl("prioritynum") = stepReourceDt.Select("resourceid='" + resSl("resourceid").ToString + "'")(0)("prioritynum")
            End If
            If recentEndDate = Nothing Then
                recentEndDate = Date.Parse(resSl("simulateEnd").ToString)
            Else
                If Date.Parse(resSl("simulateEnd").ToString) < recentEndDate Then
                    recentEndDate = Date.Parse(resSl("simulateEnd").ToString)
                End If
            End If
        Next
        resSimulationDt.AcceptChanges()

        '筛选加工时间等于最近时间，并优先级最大的设备
        Dim recResSimulationDt As DataTable = resSimulationDt.Clone '最近时间加工的

        '设备并行加工时此处不过滤设备
        For Each resSl In resSimulationDt.Rows
            If Date.Parse(resSl("simulateEnd").ToString) = recentEndDate Then
                recResSimulationDt.ImportRow(resSl)
                Exit For
            End If
        Next

        Dim recResSimulationView = recResSimulationDt.DefaultView()
        recResSimulationView.Sort = "prioritynum desc"
        recResSimulationDt = recResSimulationView.ToTable()
        recResSimulationDt.AcceptChanges()
        Return recResSimulationDt
    End Function

    ''' <summary>
    ''' 计算工步的加工周期和设备的开机周期
    ''' </summary>
    ''' <param name="conBaseRow">当前批次信息</param>
    ''' <param name="stepRow">当前工步信息</param>    
    ''' <param name="resourceTable">设备列表</param>
    ''' <remarks></remarks>
    Protected Sub GetStepCycle(ByVal conBaseRow As DataRow, ByVal stepRow As DataRow, ByRef resourceTable As DataTable)
        Dim stepCycle As Single = 0 '工序加工周期
        Dim stepCyCle2 As Single = 0 '设备开机结束周期 
        Dim containerQty As Integer = Integer.Parse(conBaseRow("qty").ToString) '批次数量
        resourceTable.Columns.Add("stepcycle")
        resourceTable.Columns.Add("stepcycle2")
        resourceTable.Columns.Add("qty")

        Dim shipcycle = If(String.IsNullOrWhiteSpace(stepRow("shipcycle").ToString()), 0, Single.Parse(stepRow("shipcycle").ToString))

        For i As Integer = 0 To resourceTable.Rows.Count - 1
            If Not String.IsNullOrWhiteSpace(resourceTable.Rows(i)("chargingqty").ToString) And resourceTable.Rows(i)("chargingqty").ToString <> "0" Then '如果装槽量不为空
                Dim chargingTimes As Integer = Math.Ceiling(containerQty / Integer.Parse(resourceTable.Rows(i)("chargingqty").ToString))
                '举例：批次30，装炉量15(如果一炉不够加工需要装多炉(一炉加工多次)) 总时间 = 准备时间 + 30/15 * 设备加工时间 + 检验时间(转运时间))
                stepCycle = Single.Parse(stepRow("transfercycle").ToString) + chargingTimes * Single.Parse(stepRow("makecycle").ToString) +
                                         Single.Parse(stepRow("preparecycle").ToString) + shipcycle
                stepCyCle2 = chargingTimes * Single.Parse(stepRow("makecycle").ToString) + Single.Parse(stepRow("preparecycle").ToString) + shipcycle
            Else
                '总时间 = 准备时间 + 设备加工时间 + 检验时间(转运时间))
                stepCycle = Single.Parse(stepRow("transfercycle").ToString) + Single.Parse(stepRow("makecycle").ToString) +
                                         Single.Parse(stepRow("preparecycle").ToString) + shipcycle
                stepCyCle2 = Single.Parse(stepRow("makecycle").ToString) + Single.Parse(stepRow("preparecycle").ToString) + shipcycle
            End If
            resourceTable.Rows(i)("stepcycle") = stepCycle
            resourceTable.Rows(i)("stepcycle2") = stepCyCle2
            resourceTable.Rows(i)("qty") = containerQty
        Next
    End Sub

    ''' <summary>
    ''' 获取批次的可排产工步
    ''' </summary>
    ''' <param name="containerID"></param>
    ''' <returns>返回对象：（是否成功，提示信息，特殊标识（可返回做特殊处理），返回的工步列表）</returns>
    ''' <remarks></remarks>
    Public Function GetCanApsStepinfoData(ByVal containerID As String) As Tuple(Of Boolean, String, Integer, DataTable)
        '************************
        '------获取批次当前工艺下的所有工步
        '************************
        Dim sql = String.Format(<stirng>
                        SELECT s.stepno,s.description,s.stepinfoid,s.stepseq,s.makecycle,s.chargingqty,s.preparecycle,s.shipcycle,s.steptype,s.transfercycle,
                        ws.workflowstepid,ws.workflowstepname,s.workcenterid,s.teamid,'{1}' stepinfofactoryid
                        FROM containercurrentinfo cci
                        LEFT JOIN container c ON c.containerid = cci.containerid
                        LEFT JOIN workflowstep ws ON ws.workflowid = DECODE(cci.isinstore,-1,c.mfgworkflowid,cci.workflowid) 
                        LEFT JOIN Userattribute u ON u.parentid = c.containerid AND u.userattributename = '开始工步顺序号' 
                        LEFT JOIN stepinfo s ON s.workflowstepid = ws.workflowstepid AND s.stepseq >= NVL(u.attributevalue,0)
                        INNER JOIN stepinfobase sb ON sb.revofrcdid = s.Stepinfoid --取最新版
                        WHERE cci.containerid = '{0}'
                        ORDER BY s.stepseq
                  </stirng>.Value, containerID, rbFactoryID)
        Dim dtStepInfo = DatabaseHelper.Query_ReturnDatTable(sql)
        If dtStepInfo.Rows.Count = 0 Then
            Return New Tuple(Of Boolean, String, Integer, DataTable)(False, "批次不存在工步信息！", 0, dtStepInfo)
        End If
        '************************
        '------查询批次所有的加工记录
        '************************
        sql = String.Format(<stirng>
                    SELECT csd.stepinfoid,s.stepno,s.description FROM containerstepdispatch2 csd 
                    LEFT JOIN stepinfo s ON s.stepinfoid = csd.stepinfoid
                    WHERE csd.containerid = '{0}' AND csd.isuse = 0
                    UNION ALL 
                    SELECT csw.stepinfoid,s.stepno,s.description FROM containerstepworkreport2 csw
                    LEFT JOIN stepinfo s ON s.stepinfoid = csw.stepinfoid
                    WHERE csw.containerid = '{0}' AND csw.isuse = 0
              </stirng>.Value, containerID)
        Dim dtStepRecode = DatabaseHelper.Query_ReturnDatTable(sql)
        '************************
        '------查询批次所有已下发的排产记录
        '************************
        sql = String.Format(<stirng>
                    SELECT ap.stepinfoid FROM apsplandetail2 ap WHERE ap.containerid = '{0}' AND ap.isuse = 0 AND ap.status = '1'
              </stirng>.Value, containerID)
        Dim dtApsDetail = DatabaseHelper.Query_ReturnDatTable(sql)
        '************************
        '------定义所需的变量
        '************************
        Dim res = True '验证结果
        Dim msg = String.Empty '验证出错提示
        Dim stepInfoIDs = String.Empty '工步ID集
        Dim dtNewStepInfo = dtStepInfo.Clone '最终可排产的工步
        '************************
        '------开始验证工步基础数据、其他版本是否加工、并筛选可排产的工步
        '************************
        For Each item As DataRow In dtStepInfo.Rows
            Dim _temp = 0
            If Not String.IsNullOrWhiteSpace(item("shipcycle").ToString()) Then
                If Not Single.TryParse(item("shipcycle").ToString(), _temp) Then
                    res = False
                    msg = "转工周期数值维护有误！"
                    Exit For
                End If
            End If

            If Not String.IsNullOrWhiteSpace(item("transfercycle").ToString()) Then
                If Not Single.TryParse(item("transfercycle").ToString(), _temp) Then '转运周期不为整数
                    res = False
                    msg = "运转周期数值维护有误！"
                    Exit For
                End If
            End If

            If String.IsNullOrWhiteSpace(item("makecycle").ToString()) Then '加工周期必填
                res = False
                msg = "准备周期数值不能为空！"
                Exit For
            End If

            If Not Single.TryParse(item("makecycle").ToString(), _temp) Then '加工周期不为整数或小数
                res = False
                msg = "加工周期数值维护有误！"
                Exit For
            End If

            If Not String.IsNullOrWhiteSpace(item("preparecycle").ToString()) Then
                If Not Single.TryParse(item("preparecycle").ToString(), _temp) Then '准备周期不为整数或小数
                    res = False
                    msg = "准备周期数值维护有误！"
                    Exit For
                End If
            End If
            If Not String.IsNullOrWhiteSpace(item("chargingqty").ToString()) Then
                If Not Integer.TryParse(item("chargingqty").ToString(), _temp) Then '装槽量必须整数
                    res = False
                    msg = "装槽量数值维护有误！"
                    Exit For
                End If
            End If
            '如果加工记录中存在该工步号，但不是该版本的，则提示
            If dtStepRecode.Select(String.Format("stepno = '{0}' AND stepinfoid <> '{1}'", item("stepno").ToString(), item("stepinfoid").ToString())).Length > 0 Then
                res = False
                msg = "批次存在其他版本工步的生产数据！"
                Exit For
            End If
            '如果加工记录有该工步数据，则清除之前的工步，不参与排产
            If dtStepRecode.Select(String.Format("stepinfoid = '{0}'", item("stepinfoid").ToString())).Length > 0 Then
                dtNewStepInfo.Rows.Clear() '清除之前所有的工步
                Continue For
            End If
            '如果已下发的排产记录有该工步数据，则清除之前的工步，不参与排产
            If dtApsDetail.Select(String.Format("stepinfoid = '{0}'", item("stepinfoid").ToString())).Length > 0 Then
                dtNewStepInfo.Rows.Clear() '清除之前所有的工步
                Continue For
            End If
            dtNewStepInfo.Rows.Add(item.ItemArray()) '验证无误，则添加工步
            stepInfoIDs += String.Format("'{0}',", item("stepinfoid").ToString())
        Next
        '************************
        '------验证有误，直接返回
        '************************
        If Not res Then '
            Return New Tuple(Of Boolean, String, Integer, DataTable)(res, msg, 0, Nothing)
        End If
        '************************
        '------如果筛选后没有工步，表示所有工步都已加工或已下发
        '************************
        If dtNewStepInfo.Rows.Count = 0 Then
            Return New Tuple(Of Boolean, String, Integer, DataTable)(False, "没有可以计算的工步数据！", 1, dtNewStepInfo)
        End If
        '************************
        '------删除掉不是最新版本工步的所有排产数据
        '************************
        stepInfoIDs = stepInfoIDs.TrimEnd(",")
        Dim sqlList As New ArrayList
        sqlList.Add(String.Format("DELETE apsplaninfo ai WHERE ai.apsplaninfoid IN ( SELECT DISTINCT ap.apsplaninfoid FROM apsplandetail2 ap WHERE ap.containerid = '{0}' AND ap.stepinfoid NOT IN ({1}))", containerID, stepInfoIDs))
        sqlList.Add(String.Format("DELETE FROM apsplandetail2 ap WHERE ap.containerid = '{0}' AND ap.stepinfoid NOT IN ({1})", containerID, stepInfoIDs))
        DatabaseHelper.ExecuteSqlTran(sqlList)
        '************************
        '------返回成功数据
        '************************
        Return New Tuple(Of Boolean, String, Integer, DataTable)(True, "获取成功", 0, dtNewStepInfo)
    End Function

    ''' <summary>
    ''' 获取批次排产开始时间
    ''' </summary>
    ''' <param name="startTime"></param>
    ''' <param name="conBaseRow"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetConApsStartTime(ByVal conBaseRow As DataRow) As Date
        Dim startTime = Date.Parse(conBaseRow("STARTTIME")) '开始日期默认为批次的计划开始日期
        '查询排产池的日期规则
        Dim apsTimeRule As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format("select * from ApsPoolTimeRule where ApsPoolID='{0}' order by Sequence", conBaseRow("ApsPoolID").ToString()))
        'Dim nowDate As Date = Date.Parse(Date.Now.ToString("yyyy-MM-dd") + " 23:59:59") '记录当前日期
        Dim nowDate As Date = Date.Now
        '开始循环找到优先级最高的开始日期
        For i As Integer = 0 To apsTimeRule.Rows.Count - 1
            If apsTimeRule.Rows(i)("TimeRule").ToString = "1" Then '人为指定
                If Not conBaseRow.IsNull("ExpectMaterialDate") Then '有人为指定的日期
                    If Date.Parse(conBaseRow("ExpectMaterialDate").ToString) < nowDate Then '如果该日期过期，则继续下一个规则
                        Continue For
                    End If
                    startTime = Date.Parse(conBaseRow("ExpectMaterialDate").ToString).AddHours(Val(apsTimeRule.Rows(i)("AddTime").ToString))
                    Exit For
                ElseIf Not conBaseRow.IsNull("shipplanstarttime") Then '转工工序排产开始时间不为空
                    Dim shiPlanDate = Date.Parse(conBaseRow("shipplanstarttime").ToString).ToString("yyyy-MM-dd") '获取转工开始的日期（不要时间部分）
                    Dim expectMaterialDate = Date.Parse(String.Format("{0} 09:00:00", shiPlanDate)).AddDays(1) '指定日期默认为开始日期往后一天后的9点
                    If Date.Parse(conBaseRow("shipplanstarttime").ToString) < Date.Parse(String.Format("{0} 08:00:00", shiPlanDate)) Then '如果该日期小于八点，则指定日期改为当前日期的9点
                        expectMaterialDate = Date.Parse(String.Format("{0} 09:00:00", shiPlanDate))
                    End If
                    If expectMaterialDate < nowDate Then '如果该日期过期，则继续下一个规则
                        Continue For
                    End If
                    startTime = expectMaterialDate
                    Exit For
                Else '实际没有人为指定
                    Continue For
                End If
            End If

            If apsTimeRule.Rows(i)("TimeRule").ToString = "2" Then '系统当前时间基础上叠加
                Dim currTime = Date.Parse(String.Format("{0}:00:00", Date.Now.ToString("yyyy-MM-dd HH"))) '暂定精准到小时（如果到秒、分钟，会导致组批时选择设备不一致，下一个批次计算时已经过了这个时间导致组批不一致问题，如果小时也导致这种情况，建议改到天）
                startTime = currTime.AddHours(Val(apsTimeRule.Rows(i)("AddTime").ToString))
                Exit For
            End If

            If apsTimeRule.Rows(i)("TimeRule").ToString = "3" Then '在订单开始基础上增加时间
                If Date.Parse(conBaseRow("mfgstarttime").ToString) < nowDate Then '如果该日期过期，则继续下一个规则
                    Continue For
                End If
                startTime = Date.Parse(conBaseRow("mfgstarttime").ToString).AddHours(Val(apsTimeRule.Rows(i)("AddTime").ToString))
                Exit For
            End If

            If apsTimeRule.Rows(i)("TimeRule").ToString = "4" Then '批次最后一个派工结束时间上叠加
                Dim conDispatchDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(<string>select csd.* from containerstepdispatch2 csd
                left join stepinfo s ON s.stepinfoid = csd.stepinfoid
                where csd.containerid='{0}' and csd.isuse=0
                order by csd.plannedfinishdate desc</string>.Value, conBaseRow("containerid").ToString, conBaseRow("workflowid").ToString))
                If conDispatchDt.Rows.Count > 0 Then
                    If Date.Parse(conDispatchDt.Rows(0)("plannedfinishdate").ToString) < nowDate Then '如果该日期过期，则继续下一个规则
                        Continue For
                    End If
                    startTime = Date.Parse(conDispatchDt.Rows(0)("plannedfinishdate").ToString).AddHours(Val(apsTimeRule.Rows(i)("AddTime").ToString))
                    Exit For
                End If
            End If
        Next
        Return startTime
    End Function

    ''' <summary>
    ''' 正排模拟加工,工步各设备模拟加工情况
    ''' </summary>
    ''' <param name="resourceTable">设备集合</param>
    ''' <param name="faMakeCalendarDt">车间日历数据</param>
    ''' <param name="stepResMKDt">设备日历数据</param>
    ''' <param name="stepShiftDt">设备班次数据</param>
    ''' <param name="stepResUpkeepDt">设备保养数据</param>
    ''' <param name="apsOccupyResDT">设备占用数据</param>
    ''' <param name="startTime">初期开始时间</param>
    ''' <param name="endTime">初期结束时间</param>     
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMAscSimulationByRes(ByVal conBaseRow As DataRow, ByVal drStepInfo As DataRow, ByVal resourceTable As DataTable, ByVal faMakeCalendarDt As DataTable, ByVal stepResMKDt As DataTable,
                                  ByVal stepShiftDt As DataTable, ByVal stepResUpkeepDt As DataTable, ByVal apsOccupyResDT As DataTable, ByVal startTime As Date, ByVal endTime As Date
                                   ) As DataTable
        Dim result As New DataTable
        result.Columns.Add("resourceid")
        result.Columns.Add("simulateStart") '模拟开始时间
        result.Columns.Add("simulateEnd") '模拟结束时间
        result.Columns.Add("simulateStart2") '设备模拟开始时间
        result.Columns.Add("simulateEnd2") '设备模拟结束时间
        result.Columns.Add("qty") '设备的装炉量
        result.Columns.Add("combinebatch") '组批参数

        '设备的放假日历,车间日历+设备本身日历,初始化为车间日历

        Dim resultC = New ConcurrentBag(Of DataTable)

        Dim paraOptions = New ParallelOptions()
        paraOptions.MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
        Parallel.For(0, resourceTable.Rows.Count, paraOptions,
                     Sub(i)
                         Dim dr = resourceTable.Rows(i)
                         Dim reourceId As String = dr("resourceid").ToString '设备ID
                         Dim stepCycle = Single.Parse(dr("stepCycle").ToString) '工步的加工周期
                         Dim stepCyCle2 = Single.Parse(dr("stepCyCle2").ToString) '设备的加工周期
                         Dim result2 = result.Clone
                         Dim resultRow As DataRow = result2.NewRow
                         resultRow("resourceid") = reourceId

                         '如果设备是组批设备，并且当前工步的数量小于上限数
                         If dr("iscombinebatch").ToString() = "1" Then
                             Dim sql = String.Format(<string>
                                                        SELECT ap.starttime,ap.endtime,ap.resstarttime,ap.resendtime,SUM(ap.qty) qty 
                                                        FROM apsplandetail2 ap 
                                                        LEFT JOIN apsplaninfo ai ON ai.apsplaninfoid = ap.apsplaninfoid
                                                        LEFT JOIN workflow w ON w.workflowid = ai.workflowid
                                                        LEFT JOIN workflowbase wb ON wb.workflowbaseid = w.workflowbaseid
                                                        WHERE ap.resourceid = '{0}' AND ap.isuse = 0 AND ap.resstarttime >= to_date('{1}','yyyy-mm-dd hh24:mi:ss')
                                                        AND ap.containerid != '{2}'
                                                     </string>.Value, reourceId, startTime, conBaseRow("containerid").ToString())
                             Dim strRules = dr("combinerule").ToString().Split(",")
                             For Each rule As String In strRules
                                 If rule = "1" Then '同子工艺（虚拟件号）
                                     sql += String.Format(" AND ai.workflowid = '{0}'", conBaseRow("workflowid").ToString())
                                 ElseIf rule = "2" Then '同工艺名（子工艺工艺名）
                                     sql += String.Format(" AND wb.workflowname = '{0}'", conBaseRow("workflowname").ToString())
                                 ElseIf rule = "3" Then '同工步名
                                     sql += String.Format(" AND ap.stepinfodesc = '{0}'", drStepInfo("description").ToString())
                                 ElseIf rule = "4" Then '同工艺参数（检测项）
                                     sql += String.Format(" AND ap.stepinfoid = '{0}'", drStepInfo("stepinfoid").ToString())
                                 End If
                             Next
                             sql += " GROUP BY ap.starttime,ap.endtime,ap.resstarttime,ap.resendtime"
                             Dim dtRes = DatabaseHelper.Query_ReturnDatTable(sql) '查询出设备所有的排产数据
                             If dtRes.Rows.Count > 0 Then
                                 Dim dtLastRes = dtRes.AsEnumerable().Where(Function(p) (Integer.Parse(p("qty").ToString()) + Integer.Parse(conBaseRow("qty").ToString())) < Integer.Parse(dr("upperlimit").ToString()))
                                 If dtLastRes.Any() Then '如果设备历史排产数据的数量加上本次工步的数量小于设备的上限数，则复制该历史排产数据
                                     Dim newDt = dtLastRes.CopyToDataTable().AsEnumerable().OrderBy(Function(p) Date.Parse(p("starttime").ToString())).First() '取最近的排产信息
                                     resultRow("simulateStart") = Date.Parse(newDt("starttime").ToString()).ToString("yyyy-MM-dd HH:mm:ss")
                                     resultRow("simulateEnd") = Date.Parse(newDt("endtime").ToString()).ToString("yyyy-MM-dd HH:mm:ss")
                                     resultRow("simulateStart2") = Date.Parse(newDt("resstarttime").ToString()).ToString("yyyy-MM-dd HH:mm:ss")
                                     resultRow("simulateEnd2") = Date.Parse(newDt("resendtime").ToString()).ToString("yyyy-MM-dd HH:mm:ss")
                                     resultRow("qty") = dr("chargingqty").ToString
                                     resultRow("combinebatch") = "1"
                                     result2.Rows.Add(resultRow)
                                 End If
                             End If
                         End If
                         '验证组批是否添加了数据，没有添加就模拟加工
                         If result2.Rows.Count = 0 Then
                             Dim simulateStart = startTime '模拟开始时间
                             Dim simulateEnd = endTime '模拟结束时间

                             Dim simulateStart2 = startTime '设备模拟开始时间
                             Dim simulateEnd2 = endTime '设备模拟结束时间


                             Dim seleResID = String.Format("resourceid = '{0}'", reourceId) '设备ID筛选

                             '+设备本身日历，设备上有日历用设备的日历，无则用工厂日历
                             Dim _stepResMKDt = faMakeCalendarDt.Copy
                             If stepResMKDt.Select(seleResID).Length > 0 Then
                                 _stepResMKDt = stepResMKDt.Select(seleResID).CopyToDataTable
                             End If

                             '取出此设备的班次加工信息
                             Dim resShiftStart = "00:00:00", resShiftEnd = "23:59:00" '设备班次最早开始和最晚结束时间
                             Dim _stepShiftDt = stepShiftDt.Clone
                             Dim _stepShiftDrs = stepShiftDt.Select(seleResID)
                             If _stepShiftDrs.Length > 0 Then
                                 _stepShiftDt = _stepShiftDrs.CopyToDataTable
                                 resShiftStart = _stepShiftDt.Rows(0)("startdate").ToString
                                 resShiftEnd = _stepShiftDt.Rows(0)("enddate").ToString
                             End If

                             '----计算设备的工作周期(分钟)
                             Dim resCycle = 1439
                             If _stepShiftDt.Rows.Count > 1 Then
                                 resCycle = Val(_stepShiftDt.Compute("sum(mins)", "").ToString())
                             End If

                             '此设备的保养计划
                             Dim resUpKeepRows = stepResUpkeepDt.Select(seleResID)
                             Dim _stepResUpkeepDt As New DataTable
                             If resUpKeepRows.Length > 0 Then
                                 _stepResUpkeepDt = resUpKeepRows.CopyToDataTable
                             End If

                             '此设备占用数据
                             Dim resApsOccupyRows = apsOccupyResDT.Select(seleResID)
                             Dim _apsOccupyResDT As New DataTable
                             If resApsOccupyRows.Length > 0 Then
                                 Dim aspOccupyDV = resApsOccupyRows.CopyToDataTable().AsDataView()
                                 aspOccupyDV.Sort = "qty DESC,starttime ASC"
                                 _apsOccupyResDT = aspOccupyDV.ToTable()
                             End If

                             '计算工步模拟加工的开始时间simulateStart
                             LMAscSLResStartDate(reourceId, simulateStart, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, conBaseRow("qty").ToString(), dr("chargingqty").ToString())
                             simulateEnd = simulateStart
                             '计算工步模拟加工的结束时间simulateEnd
                             LMAscSLResEndDate(reourceId, simulateStart, simulateEnd, _stepResMKDt, _stepResUpkeepDt.Copy, _apsOccupyResDT.Copy, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, conBaseRow("qty").ToString(), dr("chargingqty").ToString(), True)

                             resultRow("simulateStart") = simulateStart.ToString("yyyy-MM-dd HH:mm:ss")
                             resultRow("simulateEnd") = simulateEnd.ToString("yyyy-MM-dd HH:mm:ss")

                             '计算设备模拟加工的开始时间
                             'LMAscSLResStartDate(reourceId, simulateStart2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
                             simulateStart2 = simulateStart
                             simulateEnd2 = simulateStart2
                             '计算设备模拟加工的结束时间
                             LMAscSLResEndDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt.Copy, _apsOccupyResDT.Copy, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCyCle2, conBaseRow("qty").ToString(), dr("chargingqty").ToString(), True)

                             resultRow("simulateStart2") = simulateStart2.ToString("yyyy-MM-dd HH:mm:ss")
                             resultRow("simulateEnd2") = simulateEnd2.ToString("yyyy-MM-dd HH:mm:ss")
                             resultRow("qty") = dr("chargingqty").ToString
                             resultRow("combinebatch") = "0"
                             result2.Rows.Add(resultRow)
                         End If
                         resultC.Add(result2)
                     End Sub)
        For Each dr As DataTable In resultC
            result.Merge(dr)
        Next
        result.AcceptChanges()
        Return result
    End Function

    ''' <summary>
    ''' 获取设备占用信息，指定时间之后
    ''' </summary>
    ''' <param name="resIds">设备ID集</param>
    ''' <param name="startTime">开始日期</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetResOccupyDtAfterTime(ByVal resIds As String, ByVal drCon As DataRow, ByVal startTime As String) As DataTable
        Dim resOccupyAps = String.Format(<string>
                               select ad.starttime,ad.endtime,ad.resstarttime,ad.resendtime,sum(ad.qty) qty,ad.resourceid from ApsPlanDetail2 ad where ad.ResourceID in ({0}) and ad.isuse = 0 and ad.containerid != '{1}'
                               and ( ad.Resstarttime >= to_date('{2}', 'yyyy-mm-dd hh24:mi:ss') 
                                    or (ad.Resstarttime {3} to_date('{2}', 'yyyy-mm-dd hh24:mi:ss') and ad.Resendtime > to_date('{2}', 'yyyy-mm-dd hh24:mi:ss') ) 
                                   )
                               and not exists (select 1 from resourcedef rd where rd.resourceid = ad.resourceid and rd.isinfinite = 1)
                               --AND NOT EXISTS (SELECT 1 FROM containerstepworkreport2 csw WHERE csw.containerid = ad.containerid AND csw.stepinfoid = ad.stepinfoid AND csw.isuse = 0)
                               GROUP BY ad.starttime,ad.endtime,ad.resstarttime,ad.resendtime,ad.resourceid
                               ORDER BY ad.starttime
                           </string>.Value, resIds, drCon("containerid").ToString(), startTime, "<")

        'Dim resOccupyAps2 = " select ad.* from ApsPlanDetail2 ad " +
        '    " left join ApsPlanInfo ap on ap.apsplaninfoid = ad.apsplaninfoid " +
        '    " where ap.apstype2 = 2 and ad.status = 0 and ap.apstype = '1' and ap.containerid in ({2}) and ad.ResourceID in ({0}) " +
        '    "and ( ad.Resstarttime>= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
        '   "or ( " +
        '   "ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
        '   ") )" +
        '    " and not exists (select 1 from resourcedef rd where rd.resourceid=ad.resourceid and rd.isinfinite=1) "

        'Dim result As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps, resIds, startTime))

        ''conIdList超过1000的处理方式,按999进行分页
        'If conIdList.Count > 999 Then
        '    Dim pCount = Math.Ceiling(conIdList.Count / 999) '页数
        '    For i As Integer = 0 To pCount - 1
        '        Dim _conIdList = conIdList.Skip(i * 999).Take(999).ToList
        '        result.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps2, resIds, startTime, "'" + String.Join(",", _conIdList).Replace(",", "','") + "'", apsType)))
        '    Next
        'Else
        '    result.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps2, resIds, startTime, "'" + String.Join(",", conIdList).Replace(",", "','") + "'", apsType)))
        'End If

        Return DatabaseHelper.Query_ReturnDatTable(resOccupyAps)
    End Function

    ''' <summary>
    ''' 获取设备保养信息,指定时间之后
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="strStartTime"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetResUpKeepDtAfterTime(ByVal resIds As String, ByVal strStartTime As String) As DataTable
        Dim strSql = "select rk.* from ResourceUpKeepInfo rk where rk.resourceid in ({0}) and rk.upkeepstartdate>=to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
" or (" +
" rk.upkeepstartdate<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and rk.upkeependdate>to_date('{1}','yyyy-mm-dd hh24:mi:ss')" +
")"
        strSql = String.Format(strSql, resIds, strStartTime)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 设备模拟加工，开始时间计算
    ''' 命名规则:LM:有限，Asc:正排，SL:模拟运算，StartDate:开始时间
    ''' </summary>
    ''' <param name="simulateStart">设备模拟加工开始时间</param>
    ''' <param name="stepResMKDt">设备日历（包含车间日历）</param>
    ''' <param name="resUpKeepDt">设备保养信息</param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次信息详细</param>
    ''' <param name="resShiftStart">设备最早开始班次</param>
    ''' <param name="resShiftEnd">设备最晚结束班次</param>
    ''' <param name="resCycle">设备运转周期(分钟)</param>
    ''' <remarks></remarks>
    Sub LMAscSLResStartDate(ByVal resourceId As String, ByRef simulateStart As Date, ByVal stepResMKDt As DataTable, ByVal resUpKeepDt As DataTable,
                            ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                            ByVal resCycle As Single, ByVal conQty As String, ByVal chargingQty As String, Optional ByVal isFirst As Boolean = True)

        '计算设备模拟加工开始时间

        '1.考虑, 日历和班次的加工情况

        '日历因素
        simulateStart = simulateStart.AddDays(GetDateOffset(simulateStart, stepResMKDt, 0)) '检查工作日历,开始时间顺移

        '班次因素
        Dim resStart = GetRunStartOrEndDate(simulateStart, resShiftStart) '设备初期开始时间
        Dim resEnd = GetRunStartOrEndDate(simulateStart, resShiftEnd) '设备初期结束时间

        'Dim differ = GetDifferMins(simulateStart, resStart)

        If simulateStart <= resStart Then '工步开始7：30 ，设备默认开始8：30
            simulateStart = resStart
        ElseIf resStart < simulateStart And simulateStart < resEnd Then '工步开始时间在设备周期内,工序开始：9：30，而设备默认开始8：30,此时要考虑休息时间段如果9:30是休息，则又需順移

            If _stepShiftDt.Rows.Count > 0 Then
                simulateStart = GetResApsStartOrEndDate(simulateStart, _stepShiftDt)
            End If

        Else '此情况例子，工序开始时间：18：30，而设备结束时间17：30，设备开始时间就要追加一天,跨天考虑日历情况
            simulateStart = resStart
            simulateStart = simulateStart.AddDays(1)
        End If
        '日历检查
        simulateStart = simulateStart.AddDays(GetDateOffset(simulateStart, stepResMKDt, 0))

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '[`````{```]````} 或者 '[`````{````}```]情况下才会让 开始时间 偏移


        Dim resUpKeepOffset1 = 0 '保养检查后，需要偏移的分钟数，开始时间
        Dim lastUpKeepEndTime = simulateStart '设备最新保养占用的最大时间
        For Each resUpKeepRow In resUpKeepDt.Rows

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            '模拟开始时间在保养周期内才会偏移
            If Not (lastUpKeepEndTime >= upkeepStart And lastUpKeepEndTime < upkeepEnd) Then
                Continue For
            End If
            '必须大于上次占用最大时间才偏移
            If upkeepEnd <= lastUpKeepEndTime Then
                Continue For
            End If

            resUpKeepOffset1 += GetDifferMins(upkeepEnd, lastUpKeepEndTime)
            lastUpKeepEndTime = upkeepEnd
        Next

        simulateStart = simulateStart.AddMinutes(resUpKeepOffset1)

        '工序设备被占用情况
        Dim resApsOccupyOffset1 = 0 '排产占用后，需要偏移的分钟数
        Dim lastOccupyEndTime = simulateStart '设备最新占用的最大时间
        For Each resApsRow In _apsOccupyResDT.Rows
            Dim apsStartTime = Date.Parse(resApsRow("resstarttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("resendtime").ToString)

            If Integer.Parse(resApsRow("qty").ToString()) + Integer.Parse(conQty) <= Integer.Parse(chargingQty) Then '如果批次数量加上设备已占的数据小于等于设备的装炉量，则表示可以在这个时间段加工
                Exit For
            End If

            '模拟开始时间在占用周期内才会偏移
            If Not (lastOccupyEndTime >= apsStartTime And lastOccupyEndTime < apsEndTime) Then
                Continue For
            End If
            '必须大于上次占用最大时间才偏移
            If apsEndTime <= lastOccupyEndTime Then
                Continue For
            End If


            resApsOccupyOffset1 += GetDifferMins(apsEndTime, lastOccupyEndTime)
            lastOccupyEndTime = apsEndTime
        Next

        simulateStart = simulateStart.AddMinutes(resApsOccupyOffset1)

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset1 = 0 And resApsOccupyOffset1 = 0) Then
            '开始递归了，防止死循环 
            If isFirst Then
                recursionStartTime = Date.Now
            End If
            '递归超过5分钟，不递归 
            If (Date.Now - recursionStartTime).TotalMinutes < 5 Then
                LMAscSLResStartDate(resourceId, simulateStart, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, conQty, chargingQty, False)
            Else '超过5分钟的递归，判定为死循环，记录日志
                LogHelp.WriteLog("排产正排出现死循环,LimitedCapacityBusiness.LMAscSLResStartDate各参数为" +
                                 "resourceId：" + resourceId +
                                 "simulateStart：" + simulateStart.ToString("yyyy-MM-dd HH:mm:ss") +
                                 "stepResMKDt：" + JsonConvert.SerializeObject(stepResMKDt) +
                                 "resUpKeepDt：" + JsonConvert.SerializeObject(resUpKeepDt) +
                                 "_apsOccupyResDT：" + JsonConvert.SerializeObject(_apsOccupyResDT) +
                                 "_stepShiftDt" + JsonConvert.SerializeObject(_stepShiftDt) +
                                 "resShiftStart：" + resShiftStart +
                                 "resShiftEnd：" + resShiftEnd +
                                 "resCycle：" + resCycle.ToString() +
                                 "isFirst：" + isFirst.ToString()
                                 )
            End If
        End If
    End Sub

    ''' <summary>
    ''' 设备模拟加工，结束时间计算
    ''' 命名规则:LM:有限，Asc:正排，SL:模拟运算，EndDate:结束时间
    ''' </summary>
    ''' <param name="simulateStart"></param>
    ''' <param name="simulateEnd"></param>
    ''' <param name="stepResMKDt"></param>
    ''' <param name="resUpKeepDt"></param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次信息详细</param>
    ''' <param name="resShiftStart">设备最早开始班次</param>
    ''' <param name="resShiftEnd">设备最晚结束班次</param>
    ''' <param name="resCycle"></param>
    ''' <param name="stepCycle"></param>
    ''' <param name="isFirst"></param>
    ''' <remarks></remarks>
    Sub LMAscSLResEndDate(ByVal resourceId As String, ByVal simulateStart As Date, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable,
                          ByVal resUpKeepDt As DataTable, ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                          ByVal resCycle As Integer, ByVal stepCycle As Single, ByVal conQty As String, ByVal chargingQty As String, ByVal isFirst As Boolean)

        simulateEnd = simulateEnd.AddDays(GetDateOffset(simulateEnd, stepResMKDt, 0))
        '1.考虑, 日历和班次的加工情况
        If isFirst Then
            Dim resRunCycle = stepCycle \ resCycle '设备模拟运行周期取整
            If resRunCycle = 0 Then

            Else '跨天，考虑日历放假情况

                Dim offset = 0 '偏移量
                For i As Integer = 1 To resRunCycle
                    offset = GetDateOffset(simulateEnd, stepResMKDt, 1)
                    simulateEnd = simulateEnd.AddDays(offset + 1)
                Next

            End If

            Dim resRunCycle2 = stepCycle Mod resCycle '设备运行周期取余数
            '追加余数时，可能会受中间休息段影响，eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
            simulateEnd = GetResApsEndOrStartDate(simulateEnd, resRunCycle2, _stepShiftDt, stepResMKDt)
        Else '递归进来的情况
            '班次因素
            Dim resEnd = GetRunStartOrEndDate(simulateEnd, resShiftEnd) '设备结束时间
            Dim resStart = GetRunStartOrEndDate(simulateEnd, resShiftStart) '设备开始时间

            If resEnd <= resStart Then 'add:Wangjh 2023/2/15
                '开始时间大于结束时间，表示跨天
                resEnd = resEnd.AddDays(1)
            End If

            If resEnd < simulateEnd Then '例如：工序18：30结束，而设备默认17：30结束，追加一天
                Dim differ = GetDifferMins(simulateEnd, resEnd)

                simulateEnd = resEnd
                simulateEnd = simulateEnd.AddDays(1)
                simulateEnd = simulateEnd.AddDays(GetDateOffset(simulateEnd, stepResMKDt, 0))

                simulateEnd = GetResApsEndOrStartDate(simulateEnd, differ, _stepShiftDt, stepResMKDt)
            ElseIf simulateEnd < resStart Then '结束时间，在设备未开始，eg:工序7:30结束，而设备8：30开始----->实际结束：8：30+1h
                Dim differ = GetDifferMins(resStart, simulateEnd)
                simulateEnd = GetResApsEndOrStartDate(simulateEnd, differ, _stepShiftDt, stepResMKDt)
            Else '正常情况，结束时间在设备工作范围内
                '1.2考虑班次中间有休息时间段情况 eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
                simulateEnd = GetResApsStartOrEndDate(simulateEnd, _stepShiftDt)
            End If

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````]````} 或者 '{``````[````}`````] 情况下才会只让 结束时间 偏移

        Dim resUpKeepOffset2 As Single = 0 '保养检查后，需要偏移的分钟数，结束时间--注意需是否要计算出占用设备的实际工作时间是多少分钟?
        'Dim removeRowIndex As New List(Of Integer) '需要移除的行
        For i As Integer = resUpKeepDt.Rows.Count - 1 To 0 Step -1
            Dim resUpKeepRow = resUpKeepDt.Rows(i)

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            If upkeepStart >= simulateStart And upkeepStart <= simulateEnd Then
                If upkeepEnd <= simulateEnd Then '{`````[````]````}
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)

                    resUpKeepDt.Rows.RemoveAt(i)
                ElseIf upkeepEnd > simulateEnd Then '{``````[````}`````]
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)

                    resUpKeepDt.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                Dim _resUpKeepOffset2 = GetActualOccupyMins2(upkeepStart, upkeepEnd, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)
                resUpKeepOffset2 += _resUpKeepOffset2
                simulateEnd = GetResApsEndOrStartDate(simulateEnd, _resUpKeepOffset2, _stepShiftDt, stepResMKDt)
            End If

        Next

        resUpKeepDt.AcceptChanges()

        '设备占用情况，指定设备，指定时间是否被占用，在下发的计划里查,此处逻辑可行？

        Dim resApsOccupyOffset2 As Single = 0 '排产占用后，需要偏移的分钟数 --注意需是否要计算出占用设备的实际工作时间是多少分钟?
        For i As Integer = _apsOccupyResDT.Rows.Count - 1 To 0 Step -1
            Dim resApsRow = _apsOccupyResDT.Rows(i)
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            If Integer.Parse(resApsRow("qty").ToString()) + Integer.Parse(conQty) <= Integer.Parse(chargingQty) Then '如果批次数量加上设备已占的数据小于等于设备的装炉量，则表示可以在这个时间段加工
                Exit For
            End If

            If apsStartTime >= simulateStart And apsStartTime <= simulateEnd Then
                If apsEndTime <= simulateEnd Then
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                ElseIf apsEndTime > simulateEnd Then
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                Dim _resApsOccupyOffset2 = GetActualOccupyMins2(apsStartTime, apsEndTime, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)
                resApsOccupyOffset2 += _resApsOccupyOffset2
                simulateEnd = GetResApsEndOrStartDate(simulateEnd, _resApsOccupyOffset2, _stepShiftDt, stepResMKDt)
            End If
        Next

        _apsOccupyResDT.AcceptChanges()

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset2 = 0 And resApsOccupyOffset2 = 0) Then
            If isFirst Then
                recursionStartTime = Date.Now
            End If
            If (Date.Now - recursionStartTime).TotalMinutes < 5 Then
                LMAscSLResEndDate(resourceId, simulateStart, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, conQty, chargingQty, False)
            Else '超过5分钟的递归，判定为死循环，记录日志
                LogHelp.WriteLog("排产正排出现死循环,LimitedCapacityBusiness.LMAscSLResEndDate各参数为：" +
                                 "resourceId：" + resourceId +
                                 "simulateStart：" + simulateStart.ToString("yyyy-MM-dd HH:mm:ss") +
                                 "simulateEnd：" + simulateEnd.ToString("yyyy-MM-dd HH:mm:ss") +
                                 "stepResMKDt：" + JsonConvert.SerializeObject(stepResMKDt) +
                                 "resUpKeepDt：" + JsonConvert.SerializeObject(resUpKeepDt) +
                                 "_apsOccupyResDT：" + JsonConvert.SerializeObject(_apsOccupyResDT) +
                                 "_stepShiftDt" + JsonConvert.SerializeObject(_stepShiftDt) +
                                 "resShiftStart：" + resShiftStart +
                                 "resShiftEnd：" + resShiftEnd +
                                 "resCycle：" + resCycle.ToString() +
                                 "stepCycle：" + stepCycle.ToString() +
                                 "isFirst：" + isFirst.ToString()
                                 )
            End If
        End If

    End Sub

    ''' <summary>
    ''' 日期偏移量，用于假期时自动偏移
    ''' </summary>
    ''' <param name="d">日期</param>
    ''' <param name="makeCalendarDt">日历</param>
    ''' <param name="add">日期再次叠加量</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetDateOffset(ByVal d As Date, ByVal makeCalendarDt As DataTable, ByVal add As Integer) As Integer
        Dim offset = 0 '日期偏移量
        While makeCalendarDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = d.AddDays(offset + add).Date).AsDataView.Count > 0
            offset += 1
        End While
        Return offset
    End Function

    ''' <summary>
    ''' 根据传入时间和班次，获取真正开始或结束时间
    ''' eg:2022-5-20 08:30:00
    ''' </summary>
    ''' <param name="d"></param>
    ''' <param name="workshift">格式例子：08:30:00</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetRunStartOrEndDate(ByVal d As Date, ByVal workshift As String) As Date
        Dim result = d.Date
        result = result.AddHours(Val(workshift.Split(":")(0)))
        result = result.AddMinutes(Val(workshift.Split(":")(1)))
        result = result.AddSeconds(Val(workshift.Split(":")(2)))

        Return result
    End Function

    ''' <summary>
    ''' 获取两个时间相差的分钟总数，向上取整
    ''' </summary>
    ''' <param name="endDate"></param>
    ''' <param name="startDate"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetDifferMins(ByVal endDate As Date, ByVal startDate As Date) As Single
        Dim re = (endDate - startDate).TotalSeconds
        Return Single.Parse(Math.Ceiling(Decimal.Parse(re) / 60))
    End Function

    ''' <summary>
    ''' 计算实际占用的分钟数
    ''' </summary>
    ''' <param name="occupyStart">占用开始时间</param>
    ''' <param name="occupyEnd">占用结束时间</param>
    ''' <param name="resShiftStart">班次最早开始时间</param>
    ''' <param name="resShiftEnd">班次最晚结束时间</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <param name="stepResMKDt">日历</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetActualOccupyMins2(ByVal occupyStart As Date, ByVal occupyEnd As Date, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                                 ByVal stepShiftDt As DataTable, ByVal stepResMKDt As DataTable) As Single
        Dim _occupyStart = occupyStart, _occupyEnd = occupyEnd
        Dim re = 0
        '计算实际占用分钟数，考虑班次，休息时间
        Dim _days = (occupyEnd.Date - occupyStart.Date).TotalDays
        For j As Integer = 0 To _days
            _occupyStart = occupyStart.Date.AddDays(j)
            _occupyEnd = occupyStart.Date.AddDays(j + 1).AddMinutes(-1)

            '如果此占用的日期正在放假，不计算
            If stepResMKDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = _occupyStart.Date).AsDataView.Count > 0 Then
                Continue For
            End If

            If j = 0 Then
                _occupyStart = occupyStart
            End If
            If j = _days Then
                _occupyEnd = occupyEnd
            End If

            re += GetActualOccupyMins(_occupyStart, _occupyEnd, resShiftStart, resShiftEnd, stepShiftDt)

        Next
        Return re
    End Function

    ''' <summary>
    ''' 计算实际占用的分钟数,占用开始和结束在同天的情况
    ''' </summary>
    ''' <param name="occupyStart">占用开始时间</param>
    ''' <param name="occupyEnd">占用结束时间</param>
    ''' <param name="resShiftStart">班次最早开始时间</param>
    ''' <param name="resShiftEnd">班次最晚结束时间</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetActualOccupyMins(ByVal occupyStart As Date, ByVal occupyEnd As Date, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                                 ByVal stepShiftDt As DataTable) As Single
        If stepShiftDt.Rows.Count = 0 Then
            Return GetDifferMins(occupyEnd, occupyStart)
        End If
        Dim occupyMins As Single = 0 '实际占用的分钟数
        Dim occupyRestMins As Single = 0 '占用的休息时间
        Dim resStart = GetRunStartOrEndDate(occupyStart, resShiftStart) '设备最早开始时间
        Dim resEnd = GetRunStartOrEndDate(occupyStart, resShiftEnd) '设备最晚结束时间
        If resEnd <= resStart Then
            resEnd = resEnd.AddDays(1)
        End If

        Dim _stepShiftDt = stepShiftDt.Copy()
        For i As Integer = 0 To _stepShiftDt.Rows.Count - 1
            Dim stepShiftRow = _stepShiftDt.Rows(i)
            Dim startdatedetail = GetRunStartOrEndDate(occupyStart, stepShiftRow("startdatedetail").ToString)
            Dim enddatedetail = GetRunStartOrEndDate(occupyStart, stepShiftRow("enddatedetail").ToString)

            If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                enddatedetail = enddatedetail.AddDays(1)
            End If
            stepShiftRow("startdatedetail") = startdatedetail.ToString("yyyy/MM/dd HH:mm:ss")
            stepShiftRow("enddatedetail") = enddatedetail.ToString("yyyy/MM/dd HH:mm:ss")
        Next
        _stepShiftDt.AcceptChanges()
        '注释说明{：设备最早开始时间，}：设备最晚结束时间。[：占用开始时间,]:占用结束时间

        If Not (resEnd < occupyStart And occupyEnd < resStart) Then '此种情况下产生实际占用
            If occupyEnd > resEnd Then '[````{```````````}````] 或者 '{``````[````}`````] 情况下才会占用有效时间段
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If occupyStart >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyStart <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        occupyMins += GetDifferMins(Date.Parse(stepShiftRow("enddatedetail").ToString), occupyStart)
                    Else
                        If occupyStart < Date.Parse(stepShiftRow("startdatedetail").ToString) Then
                            occupyMins += GetDifferMins(Date.Parse(stepShiftRow("enddatedetail").ToString), Date.Parse(stepShiftRow("startdatedetail").ToString))
                        End If
                    End If
                Next
            Else '[````{```````````]````} 或者 '{``````[```````]``} 情况
                '取有效结束时间
                Dim occupyEndTemp = occupyEnd
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If occupyEndTemp >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyEndTemp <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        Exit For
                    Else
                        If i > 0 Then
                            If occupyEndTemp >= Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString) Then
                                occupyEndTemp = Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString)
                            End If
                        End If
                    End If
                Next
                '计算有效占用时间
                For i As Integer = _stepShiftDt.Rows.Count - 1 To 0 Step -1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    If Not (occupyEndTemp >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyEndTemp <= Date.Parse(stepShiftRow("enddatedetail").ToString)) Then
                        Continue For
                    End If

                    If occupyStart >= Date.Parse(stepShiftRow("startdatedetail").ToString) And occupyStart <= Date.Parse(stepShiftRow("enddatedetail").ToString) Then
                        occupyMins += GetDifferMins(occupyEndTemp, occupyStart)
                    Else
                        If occupyStart < Date.Parse(stepShiftRow("startdatedetail").ToString) Then
                            occupyMins += GetDifferMins(occupyEndTemp, Date.Parse(stepShiftRow("startdatedetail").ToString))
                        End If
                        If i > 0 Then
                            occupyEndTemp = Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail").ToString)
                        End If
                    End If
                Next
            End If

        End If


        Return occupyMins
    End Function

    ''' <summary>
    ''' 当时间在班次的最早开始和最晚结束时间内时，可用此方法
    ''' </summary>
    ''' <param name="d"></param>
    ''' <param name="stepShiftDt">班次详细列表</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetResApsStartOrEndDate(ByVal d As Date, ByVal stepShiftDt As DataTable) As Date
        If stepShiftDt.Rows.Count = 0 Then
            Return d
        End If

        Dim _stepShiftDt = stepShiftDt.Copy

        Dim resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
        Dim resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间

        For Each stepShiftRow In _stepShiftDt.Rows
            Dim startdatedetail = GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString)
            Dim enddatedetail = GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)

            If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                enddatedetail = enddatedetail.AddDays(1)
            End If

            If d >= startdatedetail And d <= enddatedetail Then '在有效加工时间内
                Return d
            End If

            stepShiftRow("startdatedetail") = startdatedetail.ToString("yyyy/MM/dd HH:mm:ss")
            stepShiftRow("enddatedetail") = enddatedetail.ToString("yyyy/MM/dd HH:mm:ss")

        Next
        _stepShiftDt.AcceptChanges()

        '到此说明不在有效加工时间内
        If d < resStart Then
            d = resStart
        ElseIf d > resEnd Then
            d = resStart.AddDays(1)
        Else
            If _stepShiftDt.Rows.Count >= 2 Then
                For i As Integer = 1 To _stepShiftDt.Rows.Count - 1
                    If d > Date.Parse(_stepShiftDt.Rows(i - 1)("enddatedetail")) And d < Date.Parse(_stepShiftDt.Rows(i)("startdatedetail")) Then '判断在哪个休息时间段
                        d = Date.Parse(_stepShiftDt.Rows(i)("startdatedetail"))
                    End If
                Next
            End If
        End If
        Return d
    End Function

    ''' <summary>
    ''' 当时间需要累加分钟数时，可用此方法 eg:班次时间段为 8:30-12:00,13:00-16:00,16:30-17:30三个时间段,考虑了班次跨天的情况
    ''' 正排的结束时间
    ''' </summary>
    ''' <param name="d"></param>
    ''' <param name="addMinites">追加分钟数</param>
    ''' <param name="stepShiftDt">班次</param>
    ''' <param name="stepResMKDt">日历</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function GetResApsEndOrStartDate(ByVal d As Date, ByVal addMinites As Single, ByVal stepShiftDt As DataTable, ByVal stepResMKDt As DataTable) As Date
        Dim orgD = d
        If stepShiftDt.Rows.Count = 0 Then
            d = d.AddMinutes(addMinites)
        End If

        If stepShiftDt.Rows.Count > 0 Then
            Dim _stepShiftDt = stepShiftDt.Copy
            Dim resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
            Dim resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间

            Dim recursionStartTime = Date.Now
            If resEnd < resStart Then
                resEnd = resEnd.AddDays(1)
            End If
            While (addMinites > 0)
                If (Date.Now - recursionStartTime).TotalMinutes > 5 Then '超过5分钟的，判定为死循环，记录日志
                    addMinites = 0
                    LogHelp.WriteLog("排产出现死循环,LimitedScheduleBaseBusiness.GetResApsEndOrStartDate各参数为：" +
                             "type：1" +
                             "d：" + d.ToString("yyyy-MM-dd HH:mm:ss") +
                             "addMinites：" + addMinites.ToString +
                             "stepShiftDt：" + JsonConvert.SerializeObject(stepShiftDt) +
                             "stepResMKDt：" + JsonConvert.SerializeObject(stepResMKDt)
                             )
                End If

                For i As Integer = 0 To _stepShiftDt.Rows.Count - 1
                    Dim stepShiftRow = _stepShiftDt.Rows(i)

                    Dim startdatedetail = GetRunStartOrEndDate(d, stepShiftRow("startdatedetail").ToString)
                    Dim enddatedetail = GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)

                    If Val(stepShiftRow("enddatedetail").ToString.Split(":")(0)) < Val(stepShiftRow("startdatedetail").ToString.Split(":")(0)) Then '跨天
                        enddatedetail = enddatedetail.AddDays(1)
                    End If

                    Dim _differ = 0
                    If (d >= startdatedetail And d <= enddatedetail) Or
                        (d > GetRunStartOrEndDate(d.AddDays(-1), stepShiftRow("startdatedetail").ToString) And d < GetRunStartOrEndDate(d, stepShiftRow("enddatedetail").ToString)) Then '在此有效加工时间内
                        _differ = GetDifferMins(enddatedetail, d)
                        If addMinites > _differ Then
                            If i < _stepShiftDt.Rows.Count - 1 Then '下一个时间段的开始时间
                                d = GetRunStartOrEndDate(d, stepShiftDt.Rows(i + 1)("startdatedetail").ToString)
                            Else '此时循环到最后一个时间段，第二天的最早开始时间
                                d = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString).AddDays(1)
                            End If
                            addMinites = addMinites - _differ
                        Else
                            d = d.AddMinutes(addMinites)
                            addMinites = 0
                            Exit For
                        End If
                    Else '当时间d不在有效时间内,可能小于最早开始，可能大于最晚结束。此时特殊处理，防止无限循环
                        Dim isChange = False
                        If d > resEnd Then
                            d = resStart.AddDays(1)
                            isChange = True
                        End If
                        If d < resStart Then
                            d = resStart
                            isChange = True
                        End If
                        If isChange Then
                            '重新赋值
                            resStart = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("startdate").ToString) '设备最早开始时间
                            resEnd = GetRunStartOrEndDate(d, _stepShiftDt.Rows(0)("enddate").ToString) '设备最晚结束时间
                            If resEnd < resStart Then
                                resEnd = resEnd.AddDays(1)
                            End If
                            Exit For
                        End If
                    End If

                    If i < _stepShiftDt.Rows.Count - 1 Then
                        If d > enddatedetail And d < GetRunStartOrEndDate(d, stepShiftDt.Rows(i + 1)("startdatedetail").ToString) Then '异常，防止无限循环
                            addMinites = 0
                        End If
                    End If

                Next
            End While
            '考虑日历
            If stepResMKDt.Rows.Count > 0 Then
                Dim offset = 0 '偏移量
                For i As Integer = 1 To (d.Date - orgD.Date).TotalDays
                    offset = GetDateOffset(orgD.AddDays(i), stepResMKDt, 0)
                    d = d.AddDays(offset)
                Next
            End If
        End If
        Return d
    End Function

#Region "基础数据获取"
    ''' <summary>
    ''' 获取批次基本信息，用于aps计算
    ''' </summary>
    ''' <param name="containerName"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetConBaseDt(ByVal containerName As String) As DataTable
        Dim sql = <string>   
                                SELECT NVL(cci.apspoolid,ao.apspoolid) apspoolid,cci.containerid,cci.containername,cci.qty,f.makecalendarid,cci.togetherflag,cci.containertype,m.plannedstartdate mfgstarttime,
                                cci.ExpectMaterialDate, w.workflowid,wb.workflowname,c.rbtogetherflag,cci.productid,cci.productname,fac.containername facontainername,ap.starttime shipplanstarttime,
                                NVL(ap.starttime,m.plannedstartdate) starttime
                                FROM containercurrentinfo cci
                                LEFT JOIN container c ON c.containerid = cci.containerid
                                LEFT JOIN factory f on f.factoryid=cci.originalfactoryid 
                                LEFT JOIN mfgorder m on m.mfgorderid=cci.mfgorderid 
                                LEFT JOIN workflow w ON w.workflowid = DECODE(cci.isinstore,-1,c.mfgworkflowid,cci.workflowid)
                                LEFT JOIN workflowbase wb ON wb.workflowbaseid = w.workflowbaseid
                                LEFT JOIN apspoolworkcenter aw ON aw.workcenterid = cci.workcenterid
                                LEFT JOIN apspool ao ON ao.apspoolid = aw.apspoolid
                                LEFT JOIN containersubcontainer csc ON csc.subcontainerid = cci.containerid
                                LEFT JOIN container fac ON fac.containerid = csc.containerid --父批次信息
                                LEFT JOIN apsplandetail ap ON ap.containerid = fac.containerid AND ap.workflowstepname = csc.workflowstepname AND ap.isuse = 0 AND NVL(ap.status,'0') = '3'
                                WHERE cci.containername = '{0}'</string>.Value
        sql = String.Format(sql, containerName)
        Return DatabaseHelper.Query_ReturnDatTable(sql)
    End Function

    ''' <summary>
    ''' 获取日历基本信息
    ''' </summary>
    ''' <param name="id"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetMakeCalendarDt(ByVal id As String) As DataTable
        Dim sql = <string>select ml.Holidaydate from MakeCalendarDetail ml where ml.MakeCalendarID='{0}' and ml.isuse=0</string>.Value
        sql = String.Format(sql, id)
        Return DatabaseHelper.Query_ReturnDatTable(sql)
    End Function

    ''' <summary>
    ''' 获取排产池基本信息
    ''' </summary>
    ''' <param name="id"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetApsPoolDt(ByVal id As String) As DataTable
        Dim sql = <string>select al.* from apspool al where al.apspoolid='{0}'</string>.Value

        sql = String.Format(sql, id)
        Return DatabaseHelper.Query_ReturnDatTable(sql)
    End Function

    ''' <summary>
    ''' 获取工步的设备信息
    ''' </summary>
    ''' <param name="stepInfoID"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Aps_GetSpecResourceDt(ByVal stepInfoID As String, ByVal productName As String) As DataTable
        Dim strSql = <string>
                    select decode(rd.resourcename,null,'',rd.description||'('||rd.resourcename||')') RESNAME,
                    sc.stepid,rd.resourceid,rd.resourcename,rd.description,rd.makecalendarid,rd.workshiftid,rd.prioritynum,
                    NVL(rd.Iscombinebatch,0) Iscombinebatch,NVL(rd.upperlimit,0) upperlimit,rd.combinerule,NVL(pr.chargingqty,0) chargingqty,
                    NVL(rd.isinfinite,0) isinfinite
                    from stepresourceinfo sc 
                    left join resourcedef rd on rd.resourceid=sc.resourceid --and rd.factoryid=s.factoryid 
                    left JOIN productresourcechargingqtyinfo pr ON pr.resourceid = rd.resourceid AND pr.productname = '{1}'
                    where sc.stepid = '{0}' order by rd.prioritynum desc</string>.Value
        strSql = String.Format(strSql, stepInfoID, productName)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 获取设备日历信息
    ''' </summary>
    ''' <param name="resIds"></param> 
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetResCalendarDt(ByVal resIds As String) As DataTable
        Dim strSql = <string>select rd.resourceid,md.Holidaydate from resourcedef rd
                            left join MakeCalendar mc on mc.makecalendarid=rd.makecalendarid and mc.isuse=0
                            left join MakeCalendarDetail md on md.makecalendarid=mc.makecalendarid and md.isuse=0
                            where rd.resourceid in ({0}) and nvl2(md.makecalendarid,'1','0')='1' 
                            union
                            select rd.resourceid,md.Holidaydate from resourcedef rd
                            left join MakeCalendar mc on mc.makecalendarid=rd.nextmakecalendarid and mc.isuse=0
                            left join MakeCalendarDetail md on md.makecalendarid=mc.makecalendarid and md.isuse=0
                            where rd.resourceid in ({0}) and nvl2(md.makecalendarid,'1','0')='1' 
                    </string>.Value
        strSql = String.Format(strSql, resIds)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 获取设备班次信息
    ''' </summary>
    ''' <param name="resIds"></param> 
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function Aps_GetResWorkShiftDt(ByVal resIds As String) As DataTable
        Dim strSql = <string>select distinct rd.resourceid,wf.workshiftid,wf.startdate,wf.enddate,wd.startdate startdatedetail,wd.enddate enddatedetail,wd.mins,wd.sequence from workshift wf
                            left join workshiftdetail wd on wd.workshiftid=wf.workshiftid
                            left join resourcedef rd on rd.workshiftid=wf.workshiftid
                            where rd.resourceid in ({0}) and wf.isuse=0
                            order by wd.sequence </string>.Value
        strSql = String.Format(strSql, resIds)
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function
#End Region

    ''' <summary>
    ''' 删除排产数据
    ''' </summary>
    ''' <param name="conApsID">排产ID</param>
    ''' <param name="seq">工序顺序</param>
    ''' <remarks></remarks>
    Public Sub DeleteApsPlanData(ByVal conApsID As String, ByVal seq As Integer, Optional ByVal status As String = "")
        Dim strSql = <string> select al.apsplandetailid from ApsPlanDetail2 al 
                                left join stepinfo s on s.stepinfoid = al.stepinfoid
                                where al.apsplaninfoid = '{0}' and al.isuse = 0 and al.status != '1' AND s.stepseq > {1} </string>.Value
        If Not String.IsNullOrWhiteSpace(status) Then
            strSql += " and al.status = '" + status + "'"
        End If
        Dim sqlList As New ArrayList

        Dim apsDetailDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, conApsID, seq))

        For Each row In apsDetailDt.Rows
            sqlList.Add(String.Format("delete apsplandetail2 al where al.apsplandetailid='{0}'", row("apsplandetailid").ToString))
        Next

        DatabaseHelper.ExecuteSqlTran(sqlList)

        If DatabaseHelper.GetSingle("select 1 from ApsPlanDetail2 al where al.apsplaninfoid='" + conApsID + "'") Is Nothing Then
            DatabaseHelper.ExecuteSql("delete ApsPlanInfo where ApsPlanInfoid='" + conApsID + "'")
        End If

    End Sub

    ''' <summary>
    ''' 初始化批次排产数据（删除其他版本的排产数据）
    ''' </summary>
    ''' <param name="container">批次信息</param>
    ''' <remarks></remarks>
    Public Sub DeleOtherRevApsPlanData(ByVal container As DataRow)
        Dim sql As String = <string> select ai.apsplaninfoid from ApsPlanInfo ai where ai.containerid = '{0}' and ai.workflowid != '{1}' and ai.ApsType2 = 2 and ai.ApsType = '1' and ai.Isuse=0 </string>.Value
        sql = String.Format(sql, container("containerid").ToString(), container("workflowid").ToString())
        Dim dtAps = DatabaseHelper.Query_ReturnDatTable(sql)
        If dtAps.Rows.Count > 0 Then
            For Each item As DataRow In dtAps.Rows
                Dim sqlList As New ArrayList
                'sqlList.Add(String.Format("delete apsplandetailemptime2 ae2 where ae2.apsplandetailempid in (select ae.apsplandetailempid from apsplandetailemp2 ae where ae.apsplandetailid IN (SELECT ap.apsplandetailid FROM apsplandetail2 ap WHERE ap.apsplaninfoid = '{0}'))", item("apsplaninfoid").ToString))
                'sqlList.Add(String.Format("delete apsplandetailemp2 ae where ae.apsplandetailid IN (SELECT ap.apsplandetailid FROM apsplandetail2 ap WHERE ap.apsplaninfoid = '{0}')", item("apsplaninfoid").ToString))
                sqlList.Add(String.Format("delete apsplandetail2 al where al.apsplaninfoid = '{0}'", item("apsplaninfoid").ToString))
                sqlList.Add(String.Format("delete ApsPlanInfo where ApsPlanInfoid = '{0}'", item("apsplaninfoid").ToString))
                DatabaseHelper.ExecuteSqlTran(sqlList)
            Next
        End If
        '删除有明细，但是没有批次排产的记录
        sql = <string> DELETE FROM apsplandetail2 ap WHERE ap.containerid = '{0}'
                       AND NOT EXISTS( SELECT 1 FROM apsplaninfo ai WHERE ai.apsplaninfoid = ap.apsplaninfoid)
        </string>.Value
        sql = String.Format(sql, container("containerid").ToString())
        DatabaseHelper.ExecuteSql(sql)
    End Sub
#End Region


#Region "子工艺齐套性检查"
    ''' <summary>
    ''' 获取工序的子工艺信息
    ''' </summary>
    ''' <param name="factoryId">子工艺所属分厂</param>
    ''' <param name="workflowstepID">主工艺工序关联子工艺的工序ID</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetSonStepWorkflow(ByVal factoryId As String, ByVal workflowstepID As String) As DataTable
        Dim sql As String = <string>
            select t.*,pb.PRODUCTNAME SONPRODUCTNAME,p.PRODUCTREVISION PRODUCTREVISIONSON, t.subworkflowid SONWORKFLOWID,t.subproductid SONPRODUCTID from(
            SELECT sbs.workflowid,sbs.workflowstepid,subw.productid subproductid,sbs.sonworkflowid subworkflowid,swb.workflowname subworkflowname,subw.workflowrevision subworkflowrevision FROM stepsubstep sbs
            inner join workflow subw on subw.workflowid=sbs.sonworkflowid
            left join workflowbase swb on swb.workflowbaseid=subw.workflowbaseid         
            left join workflowstep sws on sws.workflowstepid=sbs.sonworkflowstepid--子工艺的工序  
            left join specbase sb on sb.specbaseid=sws.specbaseid
            left join spec s on s.specid=nvl(sb.revofrcdid,sws.specid)----子工艺的工序
            where NVL2(s.commonsubworkflow,'1','0')='0'  
            union all
            --三层通用子工艺情况下，subproductid非通用工艺的件号，而是第二层虚拟子工艺的件号，这样创出的子卡到时批次调度无法换通用工艺的版本
            SELECT sbs.workflowid,sbs.workflowstepid,w.productid subproductid,subw.workflowid subworkflowid,swb.workflowname subworkflowname,subw.workflowrevision subworkflowrevision FROM
            (SELECT w.workflowid,w.productid  FROM workflow w
            LEFT JOIN workflowstep ws ON ws.workflowid=w.workflowid
            WHERE w.factoryid='{0}'
            GROUP BY w.workflowid,w.productid
            having count(ws.workflowstepid)=1) w--通用工艺情况下，第二层虚拟子工艺 
            LEFT JOIN workflowstep ws ON ws.workflowid=w.workflowid
            left join specbase sb on sb.specbaseid=ws.specbaseid
            left join spec s on s.specid=nvl(sb.revofrcdid,ws.specid)        
            INNER join workflow subw on subw.process_no=s.commonsubworkflow--通用孙子工艺
            inner join workflowbase swb on swb.revofrcdid=subw.workflowid
            LEFT JOIN stepsubstep sbs ON sbs.sonworkflowid=w.workflowid--第二层虚拟子工艺的关联信息
            INNER JOIN workflow fw ON fw.workflowid=sbs.workflowid--第一层工艺，主工艺
            WHERE   NVL2(s.commonsubworkflow,'1','0')='1' --AND sbs.workflowstepid='00074e80001aa1ab'
            GROUP BY sbs.workflowid,sbs.workflowstepid,subw.workflowid,w.productid,swb.workflowname,subw.workflowrevision
             ) t 
             LEFT JOIN product p ON p.PRODUCTID =t.subproductid
             LEFT JOIN productbase pb ON pb.PRODUCTBASEID =p.PRODUCTBASEID 
            where 1=1 and t.workflowstepid='{1}'
                                        </string>
        Dim subWorkflowDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(sql, factoryId, workflowstepID))
        '查询子工艺最新的工艺
        If subWorkflowDt.Rows.Count > 0 Then
            Dim m_CommonObj = New uLMSCommonBusiness()
            Dim newestSubWorkflow As DataTable = m_CommonObj.GetWorkflowInfoByNameAndRev(New Dictionary(Of String, String)() From {
                {"WorkflowName", subWorkflowDt.Rows(0)("subworkflowname").ToString()}
            })
            Dim drNewestSubWorkflow = newestSubWorkflow.AsEnumerable().OrderByDescending(Function(row) row("workflowid").ToString()).FirstOrDefault()
            subWorkflowDt.Rows(0)("subworkflowrevision") = drNewestSubWorkflow("workflowrevision").ToString()
            subWorkflowDt.Rows(0)("subworkflowid") = drNewestSubWorkflow("workflowid").ToString()
        End If
        Return subWorkflowDt
    End Function

    ''' <summary>
    ''' 工艺生产数据
    ''' </summary>
    ''' <param name="conRow">批次行数据</param>
    ''' <param name="sonDt">子工艺数据集</param>
    ''' <param name="IsComp">是否齐套</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetWorkflowProData(ByVal conRow As DataRow, ByVal sonDt As DataTable, ByRef IsComp As Integer) As DataTable
        Dim selectSQL As String = <string>
            SELECT '{0}' FACTORYNAME,'{1}' WORKFLOWSTEPNAME,'{2}'PRODUCTNAME,'{3}' STARTTIME,wb.WORKFLOWNAME WORKFLOWINFO,ws. WORKFLOWREVISION,ws.WORKFLOWTYPE   
 			FROM  WORKFLOWBASE wb 
            LEFT JOIN WORKFLOW ws ON ws.WORKFLOWBASEID =wb.WORKFLOWBASEID
            Where ws.WORKFLOWID='{4}'
            </string>
        Dim dt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(selectSQL, conRow("FACTORYNAME"), conRow("WORKFLOWSTEPNAME"), conRow("PRODUCTNAME"), conRow("STARTTIME"), sonDt.Rows(0)("SUBWORKFLOWID")))
        Dim rb_containerObj = New RbContainerBusiness
        Dim isExistFile = rb_containerObj.IsExistsWorkflowDocInfo(sonDt.Rows(0)("subworkflowname"), sonDt.Rows(0)("subworkflowrevision")) '工艺附件检查
        If sonDt.Rows.Count = 0 Then
            IsComp = 0
        End If
        If Not isExistFile Then
            IsComp = 0
        End If
        Return dt
    End Function
    ''' <summary>
    ''' 排产异常数据(工序设备，加工时间基础数据)
    ''' </summary>
    ''' <param name="conRow">批次行数据</param>
    ''' <param name="sonDt">子工艺数据集</param>
    ''' <param name="IsComp">是否齐套</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetApsExceptionData(ByVal conRow As DataRow, ByVal sonDt As DataTable, ByRef IsComp As Integer) As DataTable
        Dim selectSQL As String = <string>
             SELECT DISTINCT '{0}' FACTORYNAME,'{1}' WORKFLOWSTEPNAME,'{2}'PRODUCTNAME,'{3}' STARTTIME,wb.WORKFLOWNAME||':'||w. WORKFLOWREVISION WORKFLOWINFO,'{4}' PRODUCTID,w.WORKFLOWID,'{5}' SONPRODUCTNAME,
            CASE WHEN (SELECT count(1) FROM STEPRESOURCEINFO s2 where s2.STEPID =s3.STEPINFOID )>0 THEN '否'ELSE	'是' END ISCOMP,
            CASE WHEN s3.MAKECYCLE IS NULL OR s3.PREPARECYCLE IS NULL OR s3.TRANSFERCYCLE IS NULL THEN '是' ELSE '否' END ISCOMP2
 			FROM  WORKFLOWBASE wb 
            LEFT JOIN WORKFLOW w ON w.WORKFLOWBASEID =wb.WORKFLOWBASEID
            LEFT JOIN WORKFLOWSTEP ws  ON ws.WORKFLOWID =w.WORKFLOWID 
            LEFT JOIN specbase sb ON sb.specbaseid = ws.specbaseid
            LEFT JOIN spec s ON s.specid = NVL(sb.revofrcdid,ws.specid)
            LEFT JOIN STEPINFO s3 ON s3.WORKFLOWSTEPID =ws.WORKFLOWSTEPID
            Where ws.WORKFLOWID='{6}'
                                  </string>
        Dim Issue As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(selectSQL, conRow("FACTORYNAME"), conRow("WORKFLOWSTEPNAME"), conRow("PRODUCTNAME"), conRow("STARTTIME"), sonDt.Rows(0)("SONPRODUCTID"), sonDt.Rows(0)("SONPRODUCTNAME"), sonDt.Rows(0)("SUBWORKFLOWID")))
        Dim dv As DataView = Issue.DefaultView()
        dv.RowFilter = "ISCOMP='是' or ISCOMP2='是'"
        If dv.ToTable().Rows.Count > 0 Then
            IsComp = 0
        End If
        Return Issue
    End Function
    ''' <summary>
    ''' 辅材数据
    ''' </summary>
    ''' <param name="conRow">批次行数据</param>
    ''' <param name="sonDt">子工艺数据集</param>
    ''' <param name="IsComp">是否齐套</param>
    ''' <param name="type">是否工序主辅材</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetAuxMaterialData(ByVal conRow As DataRow, ByVal sonDt As DataTable, ByVal type As String, ByRef IsComp As Integer) As DataTable
        Dim selectSQL As String = <string>
            SELECT am.AUXMATERIALNAME,am.DESCRIPTION,SUM(QTY)QTY,T.WORKFLOWSTEPID,nvl(tg.MENGE,0) LABSTALL,SUM(QTY)-nvl(tg.MENGE,0) DIFFQTY,case when nvl(tg.MENGE,0)>=nvl(SUM(QTY),0) then '是' else '否' end ISCOMP
              FROM (
	                   SELECT T.QTY, T.SEQUENCE,T.TOOLS,T.WORKFLOWSTEPID FROM WORKFLOWSTEPTOOLS T
                         LEFT JOIN workflowstep ws ON ws.workflowstepid=t.workflowstepid 
                         WHERE ws.workflowid='{0}'
			           UNION 
			             SELECT T.QTY, T.SEQUENCE,T.TOOLS,SIO.WORKFLOWSTEPID FROM STEPINFOTOOLS T
			           LEFT JOIN STEPINFO SIO ON SIO.STEPINFOID = T.STEPINFOID
                         LEFT JOIN workflowstep ws ON ws.workflowstepid=SIO.workflowstepid
                          WHERE ws.workflowid='{0}'
			         ) T 
			        LEFT JOIN auxmaterial am on am.auxmaterialid=T.tools 
                    LEFT JOIN(SELECT SUM(tg.QTY) MENGE,tg.AUXMATERIALNAME FROM AUXMATERIAL_STOCK_INFO tg GROUP BY tg.AUXMATERIALNAME) tg on tg.AUXMATERIALNAME=am.AUXMATERIALNAME
                    WHERE am.auxmaterialid IS NOT NULL
			        GROUP BY am.AUXMATERIALNAME,T.WORKFLOWSTEPID,am.DESCRIPTION,tg.MENGE
                            </string>
        Dim selectSQL2 As String = <string>
            SELECT DISTINCT '{0}' FACTORYNAME,'{1}' WORKFLOWSTEPNAME,'{2}'PRODUCTNAME,'{3}' STARTTIME,wb.WORKFLOWNAME||':'||w. WORKFLOWREVISION WORKFLOWINFO,'{4}' PRODUCTID,w.WORKFLOWID,'{5}' SONPRODUCTNAME,		 	 
            T.AUXMATERIALNAME, t.DESCRIPTION, t.QTY, t.LABSTALL, t.ISCOMP
			FROM  WORKFLOWBASE wb 
            LEFT JOIN WORKFLOW w ON w.WORKFLOWBASEID =wb.WORKFLOWBASEID
            LEFT JOIN WORKFLOWSTEP ws  ON ws.WORKFLOWID =w.WORKFLOWID 
            LEFT JOIN specbase sb ON sb.specbaseid = ws.specbaseid
            LEFT JOIN spec s ON s.specid = NVL(sb.revofrcdid,ws.specid)
 			LEFT JOIN ({6} )T ON T.WORKFLOWSTEPID=ws.WORKFLOWSTEPID 
            Where ws.WORKFLOWID='{7}' and T.AUXMATERIALNAME is not null
        </string>
        selectSQL = String.Format(selectSQL, sonDt.Rows(0)("SUBWORKFLOWID"))
        If Not String.IsNullOrWhiteSpace(type) Then
            selectSQL = String.Format(selectSQL2, conRow("FACTORYNAME"), conRow("WORKFLOWSTEPNAME"), conRow("PRODUCTNAME"), conRow("STARTTIME"), sonDt.Rows(0)("SONPRODUCTID"), sonDt.Rows(0)("SONPRODUCTNAME"), selectSQL, sonDt.Rows(0)("SUBWORKFLOWID"))
        End If
        Dim AuxMaterial As DataTable = DatabaseHelper.Query_ReturnDatTable(selectSQL)
        If AuxMaterial.Columns.Contains("ISCOMP") Then
            Dim dv As DataView = AuxMaterial.DefaultView()
            dv.RowFilter = "ISCOMP='否'"
            If dv.ToTable().Rows.Count > 0 Then
                IsComp = 0
            End If
        End If
        Return AuxMaterial
    End Function

    ''' <summary>
    ''' 工装工具数据
    ''' </summary>
    ''' <param name="conRow">批次行数据</param>
    ''' <param name="sonDt">子工艺数据集</param>
    ''' <param name="IsComp">是否齐套</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetToolData(ByVal conRow As DataRow, ByVal sonDt As DataTable, ByRef IsComp As Integer) As DataTable
        Dim table As String = <string>
            SELECT CUTTOOLNAME TOOLNAME,DESCRIPTION,CUTTOOLID TOOLID,RESOURCETYPEID,DRAWINGNO FROM  CUTTOOL
            UNION ALL
            SELECT MEASURETOOLNAME TOOLNAME,DESCRIPTION,MEASURETOOLID TOOLID,RESOURCETYPEID,DRAWINGNO FROM  MEASURETOOL
            UNION ALL
            SELECT SPECIALTOOLNAME TOOLNAME,DESCRIPTION,SPECIALTOOLID TOOLID,RESOURCETYPEID,DRAWINGNO FROM  SPECIALTOOL
       </string>
        Dim selectSQL As String = <string>
            SELECT DISTINCT '{0}' FACTORYNAME,'{1}' WORKFLOWSTEPNAME,'{2}'PRODUCTNAME,'{3}' STARTTIME,wb.WORKFLOWNAME||':'||w. WORKFLOWREVISION WORKFLOWINFO,'{4}' PRODUCTID,w.WORKFLOWID,'{5}' SONPRODUCTNAME,			 	 
			T.TOOLNAME,t.DESCRIPTION,t.RESOURCETYPENAME,nvl(T.QTY,0) QTY, nvl(tg.MENGE,0) LABSTALL,case when nvl(tg.MENGE,0)>=nvl(T.QTY,0) then '是' else '否' end ISCOMP	 	 
			FROM  WORKFLOWBASE wb 
            LEFT JOIN WORKFLOW w ON w.WORKFLOWBASEID =wb.WORKFLOWBASEID
            LEFT JOIN WORKFLOWSTEP ws  ON ws.WORKFLOWID =w.WORKFLOWID 
 			LEFT JOIN (
                          	 SELECT SUM(QTY) QTY,CT.TOOLNAME,CT.DESCRIPTION,DRAWINGNO,RT.RESOURCETYPENAME,T.WORKFLOWSTEPID FROM (
	                          	SELECT T.QTY, T.SEQUENCE,T.TOOLS,T.WORKFLOWSTEPID FROM WORKFLOWSTEPTOOLS T
                                LEFT JOIN workflowstep ws ON ws.workflowstepid=t.workflowstepid 
                                WHERE ws.workflowid='{6}'
								UNION 
							    SELECT T.QTY, T.SEQUENCE,T.TOOLS,SIO.WORKFLOWSTEPID FROM STEPINFOTOOLS T
								LEFT JOIN STEPINFO SIO ON SIO.STEPINFOID = T.STEPINFOID
                                LEFT JOIN workflowstep ws ON ws.workflowstepid=SIO.workflowstepid 
                                WHERE ws.workflowid='{6}'
							) T 
							 LEFT JOIN ({7}) CT  ON CT.TOOLID = T.TOOLS
						 	 LEFT JOIN RESOURCETYPE RT  ON RT.RESOURCETYPEID = CT.RESOURCETYPEID WHERE CT.TOOLID IS NOT NULL 
						 	 GROUP BY CT.TOOLNAME,CT.DESCRIPTION,CT.DRAWINGNO,RT.RESOURCETYPENAME,T.WORKFLOWSTEPID
                          ) T ON T.WORKFLOWSTEPID=ws.WORKFLOWSTEPID
            LEFT JOIN(SELECT SUM(tg.QTY) MENGE,tg.TOOLNAME FROM TOOL_STOCK_INFO tg GROUP BY tg.TOOLNAME) tg on tg.TOOLNAME=t.TOOLNAME
           Where ws.WORKFLOWID='{8}' and T.TOOLNAME is not null 
        </string>
        selectSQL = String.Format(selectSQL, conRow("FACTORYNAME"), conRow("WORKFLOWSTEPNAME"), conRow("PRODUCTNAME"), conRow("STARTTIME"), sonDt.Rows(0)("SONPRODUCTID"), sonDt.Rows(0)("SONPRODUCTNAME"), sonDt.Rows(0)("SUBWORKFLOWID"), table, sonDt.Rows(0)("SUBWORKFLOWID"))
        Dim Tool As DataTable = DatabaseHelper.Query_ReturnDatTable(selectSQL)
        Dim dv As DataView = Tool.DefaultView()
        dv.RowFilter = "ISCOMP='否'"
        If dv.ToTable().Rows.Count > 0 Then
            IsComp = 0
        End If
        Return Tool
    End Function

    ''' <summary>
    ''' 设备数据
    ''' </summary>
    ''' <param name="conRow">批次行数据</param>
    ''' <param name="sonDt">子工艺数据集</param>
    ''' <param name="IsComp">是否齐套</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetResourceData(ByVal conRow As DataRow, ByVal sonDt As DataTable, ByRef IsComp As Integer) As DataTable
        Dim selectSQL As String = <string>
            Select DISTINCT '{0}' FACTORYNAME,'{1}' WORKFLOWSTEPNAME,'{2}'PRODUCTNAME,'{3}' STARTTIME,wb.WORKFLOWNAME||':'||w. WORKFLOWREVISION WORKFLOWINFO,'{4}' PRODUCTID,w.WORKFLOWID,'{5}' SONPRODUCTNAME,	
 			 r.RESOURCENAME,r.Description,rt.RESOURCETYPENAME,r.ERPSTATUS,CASE WHEN r.ERPSTATUS='报废' THEN '否' ELSE '是' END ISCOMP
 			 FROM  WORKFLOWBASE wb 
            LEFT JOIN WORKFLOW w ON w.WORKFLOWBASEID =wb.WORKFLOWBASEID
            LEFT JOIN WORKFLOWSTEP ws  ON ws.WORKFLOWID =w.WORKFLOWID 
            LEFT JOIN specbase sb ON sb.specbaseid = ws.specbaseid
            LEFT JOIN spec s ON s.specid = NVL(sb.revofrcdid,ws.specid)
            --LEFT JOIN SPECRESOURCEINFO s2 ON s2.SPECID =s.SPECID 
            LEFT JOIN STEPINFO s3 ON s3.WORKFLOWSTEPID =ws.WORKFLOWSTEPID
            LEFT JOIN STEPRESOURCEINFO s4 ON s4.STEPID =s3.STEPINFOID 
            LEFT JOIN RESOURCEDEF r ON r.RESOURCEID =s4.RESOURCEID 
            LEFT JOIN RESOURCETYPE rt ON rt.RESOURCETYPEID =r.RESOURCETYPEID 
            WHERE ws.WORKFLOWID='{6}' and r.RESOURCENAME is not null
            </string>
        Dim resource As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(selectSQL, conRow("FACTORYNAME"), conRow("WORKFLOWSTEPNAME"), conRow("PRODUCTNAME"), conRow("STARTTIME"), sonDt.Rows(0)("SONPRODUCTID"), sonDt.Rows(0)("SONPRODUCTNAME"), sonDt.Rows(0)("SUBWORKFLOWID")))
        Dim dv As DataView = resource.DefaultView()
        dv.RowFilter = "ISCOMP='否'"
        If dv.ToTable().Rows.Count > 0 Then
            IsComp = 0
        End If
        Return resource
    End Function

    ''' <summary>
    ''' 特殊过程数据
    ''' </summary>
    ''' <param name="conRow">批次行数据</param>
    ''' <param name="sonDt">子工艺数据集</param>
    ''' <param name="IsComp">是否齐套</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetSpotcheckData(ByVal conRow As DataRow, ByVal sonDt As DataTable, ByRef IsComp As Integer) As DataTable
        Dim selectSQL As String = <string>
            Select DISTINCT '{0}' FACTORYNAME,'{1}' WORKFLOWSTEPNAME,'{2}'PRODUCTNAME,'{3}' STARTTIME,wb.WORKFLOWNAME||':'||w. WORKFLOWREVISION WORKFLOWINFO,'{4}' PRODUCTID,w.WORKFLOWID,'{5}' SONPRODUCTNAME,	 
 			 SR.SPECIALCOURSESPECREPORTNO,to_char(SR.INDATE,'yyyy-MM-dd hh24:mi:ss') INDATE,CASE WHEN SR.INDATE IS NOT NULL AND SR.INDATE>SYSDATE THEN '是' ELSE '否' END ISCOMP
 			FROM  WORKFLOWBASE wb 
            LEFT JOIN WORKFLOW w ON w.WORKFLOWBASEID =wb.WORKFLOWBASEID
            LEFT JOIN WORKFLOWSTEP ws  ON ws.WORKFLOWID =w.WORKFLOWID 
            LEFT JOIN specbase sb ON sb.specbaseid = ws.specbaseid
            LEFT JOIN spec s ON s.specid = NVL(sb.revofrcdid,ws.specid)
            --LEFT JOIN APSPLANDETAIL apd ON APD.WORKFLOWSTEPID=ws.WORKFLOWSTEPID     
            --LEFT JOIN SpecialCourseSpecMapp SC ON SC.WORKFLOWSTEPID =ws.WORKFLOWSTEPID 
            --LEFT JOIN SpecialCourseSpecReport SR ON SR.SPECIALCOURSESPECREPORTID =SC.SPECIALCOURSESPECREPORTID
            LEFT JOIN (SELECT SC.WORKFLOWSTEPID,SR.SPECIALCOURSESPECREPORTNO, SR.INDATE FROM SpecialCourseSpecMapp SC 
	            LEFT JOIN SpecialCourseSpecReport SR ON SR.SPECIALCOURSESPECREPORTID =SC.SPECIALCOURSESPECREPORTID
	            WHERE sr.indate is not NULL AND sc.workflowid in ('{6}')
	            UNION ALL 
			    SELECT  sm.WORKFLOWSTEPID,sc.specialcoursespecreportno,sc.INDATE      FROM specialcoursespecmapp sm 
		        left join specialcoursespecreport sr on sr.specialcoursespecreportid =sm.specialcoursespecreportid  
		        left join ( select sk.specialcoursespecreportno,add_months(sk.indate,regexp_substr(sk.period , '\d+')*12) indate,s.code from specialcoursespeccontent s 
                inner join specialcoursespecreportbank sk on sk.specialcoursespecreportbankid =s.specialcoursespecreportbankid) sc on INSTR(sr.specialcoursespecreportno,sc.code)>0
        		where sr.indate is null and sm.workflowid in ('{6}')
            ) sr ON sr.WORKFLOWSTEPID=ws.WORKFLOWSTEPID
            WHERE nvl(s.isspotcheck,0)=1 AND ws.WORKFLOWID='{6}' and SR.SPECIALCOURSESPECREPORTNO is not null
            </string>
        Dim Spotcheck As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(selectSQL, conRow("FACTORYNAME"), conRow("WORKFLOWSTEPNAME"), conRow("PRODUCTNAME"), conRow("STARTTIME"), sonDt.Rows(0)("SONPRODUCTID"), sonDt.Rows(0)("SONPRODUCTNAME"), sonDt.Rows(0)("SUBWORKFLOWID")))
        Dim dv As DataView = Spotcheck.DefaultView()
        dv.RowFilter = "ISCOMP='否'"
        If dv.ToTable().Rows.Count > 0 Then
            IsComp = 0
        End If
        Return Spotcheck
    End Function

    ''' <summary>
    ''' 预排制造计划--复查--子工艺齐套性
    ''' </summary>
    ''' <param name="conDt">主批次数据集</param>
    ''' <param name="factorId">分厂ID</param>
    ''' <param name="EmployeeID">操作人</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function ReviewDetailData(ByVal conDt As DataTable, ByVal factorId As String, ByVal EmployeeID As String) As Boolean
        Dim arrList As New ArrayList()
        For Each row As DataRow In conDt.Rows
            Dim sonDT As DataTable = GetSonStepWorkflow(factorId, row("workflowstepid").ToString())
            If sonDT.Rows.Count = 0 Then
                row("ISWORKFLOW") = "0"
                row("ISAPS") = "0"
                row("ISTOOL") = "0"
                row("ISMATERIAL") = "0"
                row("ISRESOURCE") = "0"
                row("ISSPOTCHECK") = "0"
                row("ISNC") = "0"
                row("ISQUALITY") = "0"
            Else
                '工艺生产准备是否齐套
                Dim isworkflow As Integer = 1
                GetWorkflowProData(row, sonDT, isworkflow)
                row("ISWORKFLOW") = isworkflow.ToString()

                '排产异常是否齐套
                Dim isaps As Integer = 1
                GetApsExceptionData(row, sonDT, isaps)
                row("ISAPS") = isaps.ToString()

                '工装是否齐套
                Dim istool As Integer = 1
                GetToolData(row, sonDT, istool)
                row("ISTOOL") = istool.ToString()

                '辅材是否齐套
                Dim ismaterial As Integer = 1
                GetAuxMaterialData(row, sonDT, "1", ismaterial)
                row("ISMATERIAL") = ismaterial.ToString()

                '设备是否齐套
                Dim isresource As Integer = 1
                GetResourceData(row, sonDT, isresource)
                row("ISRESOURCE") = isresource.ToString()

                '特殊过程是否齐套
                Dim isspotcheck As Integer = 1
                GetSpotcheckData(row, sonDT, isspotcheck)
                row("ISSPOTCHECK") = isspotcheck.ToString()

                'NC程序是否齐套
                Dim isnc As Integer = 1
                GetNcProgramData(row, sonDT, isnc)
                row("ISNC") = isnc.ToString()

                '质量预警是否齐套
                Dim isquality As Integer = 1
                GetQualityData(row, sonDT, isquality)
                row("ISQUALITY") = isquality.ToString()

            End If
            If String.IsNullOrWhiteSpace(row("SHIPCONCOMPLETESETID").ToString()) Then
                Dim insertSQL As String = <string>INSERT INTO SHIPCONCOMPLETESET(SHIPCONCOMPLETESETID, CONTAINERID, WORKFLOWSTEPID,ISCOMPWORKFLOW, ISCOMPAPS, ISCOMPTOOL, ISCOMPMATERIAL, ISCOMPRESOURCE, ISCOMPSPOTCHECK,ISCOMPNC,ISCOMPQUALITY,CREATEEMPLOYEEID,CREATEDATE)
                                VALUES(sys_guid(),'{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}','{9}','{10}', sysdate)</string>
                arrList.Add(String.Format(insertSQL, row("CONTAINERID"), row("WORKFLOWSTEPID"), row("ISWORKFLOW"), row("ISAPS"), row("ISTOOL"), row("ISMATERIAL"), row("ISRESOURCE"), row("ISSPOTCHECK"), row("ISNC"), row("ISQUALITY"), EmployeeID))
            Else
                Dim updateSQL As String = <string>UPDATE SHIPCONCOMPLETESET SET ISCOMPWORKFLOW='{0}', ISCOMPAPS='{1}', ISCOMPTOOL='{2}', ISCOMPMATERIAL='{3}', 
                                 ISCOMPRESOURCE='{4}', ISCOMPSPOTCHECK='{5}',ISCOMPNC='{6}',ISCOMPQUALITY='{7}' where CONTAINERID='{8}' AND WORKFLOWSTEPID='{9}'</string>
                arrList.Add(String.Format(updateSQL, row("ISWORKFLOW"), row("ISAPS"), row("ISTOOL"), row("ISMATERIAL"), row("ISRESOURCE"), row("ISSPOTCHECK"), row("ISNC"), row("ISQUALITY"), row("CONTAINERID"), row("WORKFLOWSTEPID")))
            End If

        Next
        Return DatabaseHelper.ExecuteSqlListTran(arrList)
    End Function

    ''' <summary>
    ''' 查询转工热表分厂的工序转工信息
    ''' </summary>
    ''' <param name="dic">查询条件</param>
    ''' <param name="isPage">是否分页</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetApsProducePrepData(ByVal dic As Dictionary(Of String, String), ByVal isPage As Boolean) As uMESProcessResult
        Dim selectSQL As String = <string>
              SELECT cci.MFGORDERNAME,cci.TOGETHERFLAG,cci.CONTAINERNAME,f.FACTORYNAME,f1.FACTORYNAME ASSISFACTORYNAME,ws.WORKFLOWSTEPNAME,e.FULLNAME 
 			,to_char(apd.STARTTIME,'yyyy-MM-dd hh24:mi:ss') STARTTIME,to_char(apd.ENDTIME,'yyyy-MM-dd hh24:mi:ss') ENDTIME,cci.ApsSequence Sequence,cci.PRODUCTID,cci.CONTAINERID ,cci.WORKFLOWID,apd.WORKFLOWSTEPID,SC.SHIPCONCOMPLETESETID,cci.PRODUCTNAME,ROUND(TO_NUMBER(apd.ENDTIME - apd.STARTTIME)*24) DURATION,
 			sc.ISCOMPWORKFLOW ISWORKFLOW,sc.ISCOMPAPS ISAPS,sc.ISCOMPTOOL ISTOOL,sc.ISCOMPMATERIAL ISMATERIAL,sc.ISCOMPRESOURCE ISRESOURCE,sc.ISCOMPSPOTCHECK ISSPOTCHECK,
            Case WHEN SC.SHIPCONCOMPLETESETID is null then null  
            WHEN nvl(sc.ISCOMPWORKFLOW,0)=0 or nvl(sc.ISCOMPAPS,0)=0 or nvl(sc.ISCOMPTOOL,0)=0 or nvl(sc.ISCOMPMATERIAL,0)=0 or nvl(sc.ISCOMPRESOURCE,0)=0 or nvl(sc.ISCOMPSPOTCHECK,0)=0 or nvl(sc.ISCOMPNC,0)=0 or nvl(sc.ISCOMPQUALITY,0)=0 then 0 else 1 end ISCOMP
 			,sc.ISCOMPQUALITY ISQUALITY,sc.ISCOMPNC ISNC
            FROM containercurrentinfo cci
 			LEFT JOIN apsplaninfo api ON api.containerid = cci.containerid
            LEFT JOIN apsplandetail apd ON apd.apsplaninfoid = api.apsplaninfoid
 			LEFT JOIN WORKFLOWSTEP ws ON ws.WORKFLOWSTEPID =apd.WORKFLOWSTEPID 
 			LEFT JOIN FACTORY f ON f.FACTORYID =cci.ORIGINALFACTORYID
 			LEFT JOIN factory f1 ON f1.factoryid = apd.specfactoryid
            LEFT JOIN EMPLOYEE e ON e.EMPLOYEEID = apd.confirmempid
 			LEFT JOIN ShipConCompleteSet sc ON sc.CONTAINERID=cci.CONTAINERID AND sc.WORKFLOWSTEPID =apd.WORKFLOWSTEPID
 			WHERE   cci.isinpool=1  AND cci.containerstatus=1 and ws.WORKFLOWSTEPNAME is not null AND apd.confirmempid IS NOT NULL 
                                  </string>

        selectSQL = selectSQL + " and cci.originalfactoryid<>'" + dic("factoryId") + "' and apd.specfactoryid='" + dic("factoryId") + "' " '承制分厂

        If dic.ContainsKey("orgfactory") Then
            If Not String.IsNullOrWhiteSpace(dic("orgfactory")) Then '主制分厂
                selectSQL += " and cci.originalfactoryid='" + dic("orgfactory") + "'"
            End If
        End If

        If dic.ContainsKey("shipStarts") Then
            If Not String.IsNullOrWhiteSpace(dic("shipStarts")) Then '转工计划开始
                selectSQL += " and to_char(apd.starttime,'yyyy-MM-dd')>= '" + dic("shipStarts") + "'"
            End If
        End If

        If dic.ContainsKey("shipStarte") Then
            If Not String.IsNullOrWhiteSpace(dic("shipStarte")) Then '转工计划开始
                selectSQL += " and to_char(apd.starttime,'yyyy-MM-dd')<= '" + dic("shipStarte") + "'"
            End If
        End If

        If dic.ContainsKey("shipEnds") Then
            If Not String.IsNullOrWhiteSpace(dic("shipEnds")) Then '转工计划结束
                selectSQL += " and to_char(apd.endtime,'yyyy-MM-dd')>= '" + dic("shipEnds") + "'"
            End If
        End If

        If dic.ContainsKey("shipEnde") Then
            If Not String.IsNullOrWhiteSpace(dic("shipEnde")) Then '转工计划结束
                selectSQL += " and to_char(apd.endtime,'yyyy-MM-dd')<= '" + dic("shipEnde") + "'"
            End If
        End If

        If dic.ContainsKey("ContainerName") Then
            If Not String.IsNullOrWhiteSpace(dic("ContainerName")) Then '主卡批次
                selectSQL += " and cci.ContainerName  like '%" + dic("ContainerName") + "%'"
            End If
        End If

        If dic.ContainsKey("MfgOrderName") Then
            If Not String.IsNullOrWhiteSpace(dic("MfgOrderName")) Then '订单号
                selectSQL += " and cci.MfgOrderName like '%" + dic("MfgOrderName") + "%'"
            End If
        End If

        If dic.ContainsKey("WorkflowstepName") Then
            If Not String.IsNullOrWhiteSpace(dic("WorkflowstepName")) Then '工序
                selectSQL += " and ws.WorkflowstepName = '" + dic("WorkflowstepName") + "'"
            End If
        End If

        If dic.ContainsKey("status") Then
            If Not String.IsNullOrWhiteSpace(dic("status")) Then '状态
                If "2".Equals(dic("status")) Then
                    selectSQL += " AND (nvl(apd.status,0) = '2' OR nvl(apd.status,0) = '4')"
                Else
                    selectSQL += "AND nvl(apd.status,0) = '" + dic("status") + "'"
                End If

            End If
        End If

        Dim result As New uMESProcessResult()
        If isPage Then
            Dim pageDto As New uMESPagingDataDTO()
            pageDto.CurrentPageIndex = Integer.Parse(dic("pageIndex"))
            pageDto.PageSize = Integer.Parse(dic("pageSize"))
            pageDto.strSQL = selectSQL.ToString()
            result = DatabaseHelper.RunProcedureForPage(pageDto)
        Else
            result.Result = True
            result.DBTable = DatabaseHelper.Query_ReturnDatTable(selectSQL.ToString())
        End If
        Return result
    End Function

    ''' <summary>
    ''' 质量预警
    ''' </summary>
    ''' <param name="conRow">批次行数据</param>
    ''' <param name="sonDt">子工艺数据集</param>
    ''' <param name="IsComp">是否齐套</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetQualityData(ByVal conRow As DataRow, ByVal sonDt As DataTable, ByRef IsComp As Integer) As DataTable
        Dim selectSQL As String = <string>
           select distinct cci.mfgordername,cci.containername,ws.workflowstepname,c.reworkqty,c.lossqty,c.rectifymeasures,
            decode(c.rectifymeasures,'100%','是','否') iscomp
            from  containercheckinfo2 c  
            left join workflowstep ws on ws.workflowstepid =c.workflowstepid 
            left join containercurrentinfo cci on cci.containerid =c.containerid
            LEFT JOIN containersubcontainer csc ON csc.subcontainerid =c.containerid
            LEFT JOIN containercurrentinfo cci2 ON cci2.containerid =csc.containerid 
            where cci2.productname ='{0}' and ws.workflowstepname ='{1}'
            AND ((c.lossreason = '责任报废' AND NVL(c.lossqty, 0) != 0) OR NVL(c.reworkqty, 0) != 0) AND c.isuse = 0 
            AND cci.containerstatus = 1 AND cci.originalfactoryid = '{2}'
            </string>
        Dim Quality As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(selectSQL, conRow("productname"), conRow("workflowstepname"), rbFactoryID))
        Dim dv As DataView = Quality.DefaultView()
        dv.RowFilter = "ISCOMP='否'"
        If dv.ToTable().Rows.Count > 0 Then
            IsComp = 0
        End If
        Return Quality
    End Function

    ''' <summary>
    ''' 数控程序
    ''' </summary>
    ''' <param name="conRow">批次行数据</param>
    ''' <param name="sonDt">子工艺数据集</param>
    ''' <param name="IsComp">是否齐套</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetNcProgramData(ByVal conRow As DataRow, ByVal sonDt As DataTable, ByRef IsComp As Integer) As DataTable
        Dim selectSQL As String = <string>
            select n.nc_no,n.nc_name,n.material_mark,n.isthree,n.istypical,n.control_type,nvl(nd.equip_code,nd.filename) eqpiporfilename,nd.filepath 
            from workflow w 
            inner join ncprogram n on n.process_no =w.process_no and n.process_rev =w.workflowrevision and n.productname ='{0}'
            left join ncprogramdetaile nd on nd.parentid =n.id 
            where w.workflowid ='{1}'
            </string>
        Dim selectSpecNc As String = <string>
            select * from  workflowstep ws 
            left join specbase sb on sb.specbaseid=ws.specbaseid
            left join spec s on s.specid=nvl(sb.revofrcdid,ws.specid)
            where ws.workflowid ='{0}' and s.ncnumber ='1'
            </string>
        Dim NcSpec As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(selectSpecNc, sonDt.Rows(0)("SUBWORKFLOWID")))
        Dim NcProgram As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(selectSQL, sonDt.Rows(0)("SONPRODUCTNAME"), sonDt.Rows(0)("SUBWORKFLOWID")))
        If NcSpec.Rows.Count > 0 And NcProgram.Rows.Count = 0 Then
            IsComp = 0
        End If
        Return NcProgram
    End Function

#End Region
End Class
